Конструктор возвращает значение типа void

Обновлено: 18.04.2024

В Java приложение состоит из классов. Классы, в свою очередь, состоят из переменных. Они отвечают за хранение данных и методов, которые отвечают за поведение класса: иначе говоря, логику, которую он может предоставлять (например, обработку некоторых данных, вызов других методов, и т. д.). Говорить о таких составляющих как переменные можно долго, но мы сегодня не для этого собрались. Давайте лучше поговорим о такой составляющей класса как метод. Метод — это именованный блок кода, объявляемый внутри класса. Он содержит некоторую законченную последовательность действий (инструкций), направленных на решение отдельной задачи, который можно многократно использовать. Иными словами, метод — это некоторая функция: что-то, что умеет делать ваш класс. В других языках тоже присутствуют функции. Только в Java они являются членами классов и, согласно терминологии ООП, называются методами. Но прежде чем продолжить, давайте рассмотрим небольшой пример: Тут ничего сложного: метод Java, задача которого сформировать строку приветствия, с именем, которое мы ему передаем. Как например — Hello world! My name is Bobby Давайте как следует разберемся с построением метода, рассмотрев каждое ключевое слово в объявлении метода (слева направо). Наше первое ключевое слово — public , и оно обозначает модификатор доступа:

Модификаторы доступа

public : публичный. Методы или поля с этим модификатором общедоступны, видимы другим классам (а точнее, их методам и полям) из текущего пакета и из внешних пакетов. Это самый широкий уровень доступа из известных;

protected : к методам или переменным с этим модификатором есть доступ из любого места в текущем классе или пакете, или в классах, наследующих данный, а заодно — и методы или поля, даже если они находятся в других пакетах

Модификатор по умолчанию. Если у поля или метода класса нет модификатора, применяется модификатор по умолчанию. В таком случае поля или методы видны всем классам в текущем пакете (как protected , только с отсутствием видимости при наследовании).

private : антипод модификатора public . Метод или переменная с таким модификатором доступны исключительно в классе, в котором они объявлены.

Возвращаемое значение

Возвращаемое значение — это данные (некий результат выполнения метода), которые приходят на его место после вызова. Каждый метод имеет возвращаемое значение. Или нет?

Методы с возвращаемым значением

Это значение может быть данными любого вида: как переменной простого типа, так и ссылочного. В рассматриваемом примере мы указываем, что метод должен вернуть объект типа String , что как мы помним, является классом, описывающим строку. Второй момент здесь — слово return . Оно имеет прямое отношение к возвращаемому значению: значение, стоящее после него, будет отправлено назад, на место вызова метода, а сам же метод после return закроется. Это слово обычно идёт в последней строке метода (кроме методов с различными ветвлениями типа if, else.. .). Если написать код в следующей строке после return : то мы получим ругательства компилятора, что не очень хорошо (компилятор ерунду не посоветует). Также нужно помнить, что тип данных после с должен совпадать с объявленным в сигнатуре метода. Подробнее о return читайте здесь.

Для каких служит void в Java? Не у всех методов есть возвращаемое значение. Некоторым или нечего, или не нужно ничего возвращать. Что же тогда делать? Тогда в сигнатуре метода на место возвращаемого значения мы пишем void . Как бы выглядел наш метод без возвращаемого значения? Вы наверняка заметили, что вместе с возвращаемым значением у нас исчезло слово return Так и есть, ведь наш метод и не должен ничего возвращать. Тем не менее, его можно тут поставить, но без какого-то значения, просто return ; в последней строке. Это в целом бесполезно, поэтому в методах с void он необязателен. Тем не менее, его можно применять с пользой в void методах, например, при ветвлениях или циклах, когда нужно немедленно выйти из метода. Далее в объявлении метода у нас шло constructHelloSentence .

Названия методов

Методы в Java - 4

constructHelloSentence — название метода, отличительная особенность, по которой мы сможем отличать тот или иной метод. И, соответственно, вызывать тот или иной метод. Названия методов должны начинаться с маленькой буквы, но и использовать верблюжий стиль (CamelCase, верблюжий регистр): т.е. каждое следующее слово в названии стоит впритык к предыдущему и пишется с большой буквы. Наименования методов должны описывать метод (лучший комментарий — правильное именование). Для этого используйте глаголы или сочетания с глаголами: getCat , delete , createCar , и так далее. В пределах одного класса имена методов должны быть уникальными (не считая перегрузки методов, о чем поговорим немного ниже). Смотрим дальше разбираемый нами метод и видим ( String name )

Параметры метода

Методы могут иметь (или не иметь) определенные данные, которые будут поступать снаружи, а именно — с места, где и был вызван метод. В нашем случае мы видим, что приходит объект типа String с именем name и в дальнейшем мы используем эту переменную в нашем методе. В методе можно использовать неограниченное количество параметров, но больше 7 — не рекомендуется. Когда мы не знаем точное количество элементов, но все эти элементы нужны для одной цели и будут одного типа (например, String ), используется многоточие: Обращение к каждому элементу будет такого вида: name[0] Ничего не напоминает? Верно, массив! Ничего не изменится, если мы напишем: Обращение к элементам также будет вида: name[1] И ещё кое что. Аргументы метода могут быть final: Это значит, что ссылка name привязана к конкретному объекту String , и переопределить её нельзя. Про работу с ссылочными переменными и их взаимодействие с зарезервированным словом final можно прочитать в материале “Ссылочные типы данных в Java”.

Вызов методов

Методы в Java - 5

Итак, с созданием методов разобрались, теперь давайте поговорим об их использовании. Как вызвать метод в Java? Каждый метод в Java находится в классе. Чтобы разобраться, как устроен вызов методов в Java, возьмем класс: Так как у нас метод не является статическим (это отдельная тема для разговора, выходящая за рамки сегодняшней статьи), для его вызова нужно сперва создать объект, и уже у него вызвать метод: В аргументы нашего метода мы передали строку (имя), которую хотим видеть в результирующей строке, выведенной на экран: Также стоит напомнить, что методы можно переиспользовать то количество раз, сколько нам потребуется — ограничений нет.

Перегрузка методов

Методы в Java - 7

Предположим нам понадобился метод, выполняющий по сути ту же самую логику, но в предложении Hello world ! вместо world мы хотим вставить своё слово (строку). Но ведь у нас уже есть метод constructHelloSentence . Значит, нам нужно придумать новое название для метода, который выполняет по сути тот же функционал? Как бы не так: в этот момент на помощь нам приходит перегрузка методов. Перегрузка методов — это использование одного и того же имени метода несколько раз при его объявлении в классе. С точки зрения синтаксиса языка, не может быть двух одинаковых имен в некотором локальном пространстве. Но при этом допускается объявление методов с одинаковыми именами но отличающимися аргументами. Иными словами, класс содержит в себе перегруженные, когда есть два и более методов с одинаковыми названиями, но различными входными данными: Здесь мы видим, что методы не обязаны содержать одинаковый модификатор доступа (как и возвращаемый тип). Если вызывается перегруженный метод, то из нескольких объявленных методов компилятор автоматически определяет нужный по параметрам, которые указываются при вызове.

В этой статье рассматриваются названные методы. Дополнительные сведения об анонимных функциях см. в статье Лямбда-выражения.

Сигнатуры методов

Методы объявляются в классе, структуре или интерфейсе путем указания уровня доступа, такого как или , необязательных модификаторов, таких как или sealed , возвращаемого значения, имени метода и всех параметров этого метода. Все эти части вместе представляют собой сигнатуру метода.

Тип возврата метода не является частью сигнатуры метода в целях перегрузки метода. Однако он является частью сигнатуры метода при определении совместимости между делегатом и методом, который он указывает.

Параметры метода заключаются в скобки и разделяются запятыми. Пустые скобки указывают, что параметры методу не требуются. Этот класс содержит четыре метода:

Доступ к методу

Вызов метода в объекте аналогичен доступу к полю. После имени объекта добавьте точку, имя метода и круглые скобки. Аргументы перечисляются в этих скобках и разделяются запятыми. Таким образом, методы класса Motorcycle могут вызываться, как показано в следующем примере:

Параметры и аргументы метода

Определение метода задает имена и типы всех необходимых параметров. Когда вызывающий код вызывает метод, он предоставляет конкретные значения, называемые аргументами, для каждого параметра. Аргументы должны быть совместимы с типом параметра, но имя аргумента (если есть), используемое в вызывающем коде, не обязательно должно совпадать с именем параметра, указанным в методе. Пример:

Передача по ссылке и передача по значению

По умолчанию при передаче в метод экземпляра типа значения вместо самого этого экземпляра передается его копия. Поэтому изменения в аргументе не оказывают влияния на исходный экземпляр в вызывающем методе. Чтобы передать экземпляр типа значения по ссылке, используйте ключевое слово ref . Дополнительные сведения см. в разделе Передача параметров типа значения.

При передаче в метод объекта ссылочного типа передается ссылка на этот объект. То есть метод получает не сам объект, а аргумент, который указывает расположение объекта. При изменении члена объекта с помощью этой ссылки это изменение отражается в аргументе в вызывающем методе, даже если объект передается по значению.

Ссылочный тип создается с помощью ключевого слова class , как показано в следующем примере.

Теперь, если передать объект, основанный на этом типе, в метод, то будет передана ссылка на объект. В следующем примере объект типа SampleRefType передается в метод ModifyObject :

В этом примере, в сущности, делается то же, что и в предыдущем примере, — аргумент по значению передается в метод. Но поскольку здесь используется ссылочный тип, результат будет другим. В данном случае в методе ModifyObject изменено поле value параметра obj , а также изменено поле value аргумента, rt в методе TestRefType . В качестве выходных данных метод TestRefType отображает 33.

Дополнительные сведения о передаче ссылочных типов по ссылке и по значению см. в разделах Передача параметров ссылочного типа и Ссылочные типы.

Возвращаемые значения

Методы могут возвращать значение вызывающему объекту. Если тип возврата, указываемый перед именем метода, не void , этот метод может возвращать значение с помощью void . Инструкция с ключевым словом return , за которым следует значение, соответствующее типу возврата, будет возвращать это значение объекту, вызвавшему метод.

Ключевое слове return также останавливает выполнение метода. Если тип возврата — void , инструкцию return без значения по-прежнему можно использовать для завершения выполнения метода. Без ключевого слова return этот метод будет останавливать выполнение при достижении конца блока кода. Методы с типом возврата, отличным от void, должны использовать ключевое слово return для возврата значения. Например, в следующих двух методах ключевое слово return используется для возврата целочисленных значений.

Чтобы использовать значение, возвращаемое из метода, вызывающий метод может применять сам вызов метода везде, где будет достаточно значения того же типа. Можно также назначить возвращаемое значение переменной. Например, следующие два примера кода достигают одной и той же цели.

Использование локальной переменной, в данном случае result , для сохранения значения является необязательным. Это может улучшить читаемость кода или может оказаться необходимым, если нужно сохранить исходное значение аргумента для всей области метода.

Чтобы использовать значение, возвращаемое по ссылке из метода, необходимо объявить локальную ссылочную переменную, если планируется изменение значения. Например, если метод Planet.GetEstimatedDistance возвращает значение Double по ссылке, можно определить его как локальную ссылочную переменную с использованием кода следующего вида:

Асинхронные методы

С помощью функции async можно вызывать асинхронные методы, не прибегая к использованию явных обратных вызовов или ручному разделению кода между несколькими методами или лямбда-выражениями.

Если пометить метод с помощью модификатора async , можно использовать в этом методе инструкцию await . Когда управление достигает выражения await в асинхронном методе, управление возвращается вызывающему объекту и выполнение метода приостанавливается до завершения выполнения ожидающей задачи. После завершения задачи можно возобновить выполнение в методе.

Асинхронный метод возвращается в вызывающий объект, когда он встречает первый ожидаемый объект, выполнение которого еще не завершено, или когда выполнение асинхронного метода доходит до конца — в зависимости от того, что происходит раньше.

В следующем примере метод Main служит примером асинхронного метода с типом возврата Task. Он переходит к методу DoSomethingAsync и, поскольку он выражается в одной строке, он может опустить ключевые слова async и await . Поскольку DoSomethingAsync является асинхронным методом, задача для вызова DoSomethingAsync должна быть ожидаемой, как показывает следующая инструкция: await DoSomethingAsync(); .

Асинхронный метод не может объявить все параметры ref или out , но может вызывать методы, которые имеют такие параметры.

Определения текста выражений

Часто используются определения методов, которые просто немедленно возвращаются с результатом выражения или которые имеют единственную инструкцию в тексте метода. Для определения таких методов существует сокращенный синтаксис с использованием => :

Если метод возвращает void или является асинхронным методом, то текст метода должен быть выражением инструкции (так же, как при использовании лямбда-выражений). Свойства и индексаторы должны быть только для чтения, и вы не должны использовать ключевое слово get метода доступа.

Iterators

Итератор выполняет настраиваемую итерацию по коллекции, например по списку или массиву. Итератор использует инструкцию yield return для возврата всех элементов по одному. Когда достигается инструкция yield return , текущее расположение в коде запоминается. При следующем вызове итератора выполнение возобновляется с этого места.

Итератор вызывается из клиентского кода с помощью инструкции foreach .

Дополнительные сведения см. в разделе Итераторы.

В этом разделе рассматриваются названные методы. Дополнительные сведения об анонимных функциях см. в статье Лямбда-выражения.

Сигнатуры методов

Методы объявляются с помощью ограничений class , record или struct , для которых указываются следующие данные.

  • Уровень доступа (необязательно), например public или private . Значение по умолчанию — private .
  • Необязательные модификаторы, например abstract или sealed .
  • Возвращаемое значение или void , если у метода его нет.
  • Имя метода.
  • Любые параметры методов. Параметры метода заключаются в скобки и разделяются запятыми. Пустые скобки указывают, что параметры методу не требуются.

Вместе все эти части формируют сигнатуру метода.

Тип возврата метода не является частью сигнатуры метода в целях перегрузки метода. Однако он является частью сигнатуры метода при определении совместимости между делегатом и методом, который он указывает.

В следующем примере определяется класс с именем Motorcycle , содержащий пять методов:

Класс Motorcycle включает перегруженный метод Drive . Оба метода называются одинаково, но различаются по типам параметров.

Вызов метода

Можно использовать метод instance или static. Для того чтобы вызвать метод instance, необходимо создать экземпляр объекта и вызвать для него метод; метод instance будет применен к этому экземпляру и его данным. Вы вызываете статический метод, ссылаясь на имя типа, к которому принадлежит метод; статические методы не работают с данными экземпляра. При попытке вызвать статический метод с помощью экземпляра объекта возникает ошибка компилятора.

Вызов метода аналогичен доступу к полю. После имени объекта (при вызове метода экземпляра) или имени типа (при вызове static метода), добавьте точку, имя метода и круглые скобки. Аргументы перечисляются в этих скобках и разделяются запятыми.

Определение метода задает имена и типы всех необходимых параметров. Когда вызывающий код вызывает метод, он предоставляет конкретные значения, называемые аргументами, для каждого параметра. Аргументы должны быть совместимы с типом параметра, но имя аргумента, если оно используется в вызывающем коде, не должно совпадать с параметром, указанным в методе. В следующем примере метод Square имеет один параметр типа int с именем i. Первый вызов метода передает методу Square переменную типа int с именем num; второй — числовую константу, а третий — выражение.

В наиболее распространенной форме вызова методов используются позиционные аргументы; они передаются в том же порядке, что и параметры метода. Таким образом, методы класса Motorcycle могут вызываться, как показано в следующем примере. Например, вызов метода Drive включает два аргумента, которые соответствуют двум параметрам в синтаксисе метода. Первый становится значением параметра miles , а второй — значением параметра speed .

При вызове метода вместо позиционных аргументов можно также использовать именованные аргументы. При использовании именованных аргументов необходимо указать имя параметра, двоеточие (":"), а затем аргумент. Аргументы для метода могут отображаться в любом порядке, при условии, что все обязательные аргументы присутствуют. В следующем примере для вызова метода TestMotorcycle.Drive используются именованные аргументы. В этом примере именованные аргументы передаются из списка параметров метода в обратном порядке.

Метод можно вызывать, используя и позиционные, и именованные аргументы. Однако позиционные аргументы могут следовать за именованными аргументами, только если именованные аргументы находятся в правильных позициях. В следующем примере метод TestMotorcycle.Drive из предыдущего примера вызывается с использованием одного позиционного и одного именованного аргумента.

Унаследованные и переопределенные методы

Помимо членов, определенных в нем явно, тип наследует члены, определенные в его базовых классах. Так как все типы в системе управляемых типов напрямую или косвенно наследуются из класса Object, все типы наследуют его члены, такие как Equals(Object), GetType() и ToString(). В следующем примере определяется класс Person , который создает экземпляры двух объектов Person и вызывает метод Person.Equals , чтобы определить, равны ли эти объекты. Однако Equals метод не определен в Person классе; он наследуется от Object.

Типы могут переопределять унаследованные члены, используя ключевое слово override и обеспечивая реализацию переопределенного метода. Сигнатура метода должна быть такой же, как у переопределенного метода. Следующий пример аналогичен предыдущему за тем исключением, что переопределяет метод Equals(Object). (Он также переопределяет метод GetHashCode(), поскольку оба эти метода предназначены для получения согласованных результатов.)

Передача параметров

Передача параметров по значению

При передаче типа значения в метод по значению вместо самого объекта передается его копия. Это значит, что изменения объекта в вызываемом методе не отражаются на исходном объекте, когда управление возвращается вызывающему объекту.

Код в следующем примере передает тип значения в метод по значению, а вызываемый метод пытается изменить значение типа значения. Он определяет переменную типа int , который является типом значения, присваивает ему значение 20 и передает его в метод с именем ModifyValue , который изменяет значение переменной на 30. Однако, когда метод возвращается, значение переменной остается неизменным.

Если объект ссылочного типа передается в метод по значению, ссылка на этот объект передается по значению. Это значит, что метод получает не сам объект, а аргумент, который указывает расположение объекта. Если с помощью этой ссылки в член объекта вносится изменение, это изменение отражается в объекте, даже если управление возвращается вызывающему объекту. При этом изменения в объекте, переданном в метод, не отражаются на исходном объекте, когда управление возвращается вызывающему объекту.

В следующем примере определяется класс (ссылочного типа) с именем SampleRefType . Он создает экземпляр объекта SampleRefType , задает в его поле value значение 44 и передает объект в метод ModifyObject . В этом примере по сути то же самое, что и в предыдущем примере, он передает аргумент по значению в метод. Однако поскольку здесь используется ссылочный тип, результат будет другим. В данном случае в методе ModifyObject изменено поле obj.value , при этом поле value аргумента rt в методе Main также изменяется на 33, как видно из результатов в предыдущем примере.

Передача параметров по ссылке

Параметр передается по ссылке, когда нужно изменить значение аргумента в методе и сохранить это изменение после того, как управление вернется вызывающему методу. Для передачи параметра по ссылке используйте ключевое слово ref или out . Можно также передать значение по ссылке, чтобы предотвратить копирование, и при этом запретить внесение изменений с помощью ключевого слова in .

Следующий пример идентичен предыдущему за тем исключением, что значение передается в метод ModifyValue по ссылке. Если значение параметра в методе ModifyValue будет изменено, при возвращении управления вызывающему объекту это изменение не сохранится.

Общий шаблон, в котором используются параметры по ссылке, включает замену значений переменных. Когда две переменные передаются в метод по ссылке, он меняет их содержимое местами. В следующем примере меняются местами целочисленные значения.

Передача параметров ссылочного типа позволяет изменить значение самой ссылки, а не отдельных ее элементов или полей.

Массивы параметров

В некоторых случаях требование об указании точного числа аргументов для метода является строгим. Если параметр в массиве параметров указывается с помощью ключевого слова params , метод можно вызывать с переменным числом аргументов. Параметр, помеченный ключевым словом params , должен быть типом массива и занимать последнюю позицию в списке параметров метода.

После этого вызывающий объект можно вызвать одним из четырех способов:

  • передавая массив соответствующего типа, содержащий требуемое число элементов;
  • передавая в метод список отдельных аргументов соответствующего типа, разделенный запятыми;
  • Путем передачи null .
  • не передавая никакие аргументы в массив параметров.

В следующем примере определяется метод с именем GetVowels , возвращающий все гласные из массива параметров. Метод Main демонстрирует все четыре способа вызова метода. Вызывающие элементы не обязаны предоставлять аргументы для параметров, включающих модификатор params . В этом случае параметр является пустым массивом.

Необязательные параметры и аргументы

Определение метода может указать, что его параметры являются обязательными или что они являются необязательными. По умолчанию параметры обязательны. Для определения необязательных параметров значения параметра по умолчанию включаются в определение метода. Если при вызове метода никакие аргументы для необязательного параметры не указываются, вместо них используется значение по умолчанию.

Значение параметра по умолчанию должно быть назначено одним из следующих видов выражений:

Константа, например, строковый литерал или число.

Выражение в форме new ValType() , где ValType — это тип значения. Это вызывает неявный конструктор типа значения без параметров, который не является фактическим членом типа.

Если метод содержит как обязательные, так и необязательные параметры, необязательные параметры определяются в конце списка параметров после всех обязательных параметров.

В следующем примере определяется метод ExampleMethod , который имеет один обязательный и два необязательных параметра.

Если для вызова метода с несколькими необязательными аргументами используются позиционные аргументы, вызывающий объект должен предоставить аргумент для всех необязательных параметров, для которых предоставлен аргумент, от первого до последнего. В случае ExampleMethod метода, например, если вызывающий объект предоставляет аргумент для description параметра, он также должен предоставить его для optionalInt параметра. opt.ExampleMethod(2, 2, "Addition of 2 and 2"); — допустимый вызов метода; opt.ExampleMethod(2, , "Addition of 2 and 0"); вызывает ошибку компилятора "Аргумент отсутствует".

Если метод вызывается с помощью именованных аргументов или комбинации позиционных и именованных аргументов, вызывающий объект может опустить любые аргументы, следующие за последним позиционным аргументом в вызове метода.

В следующем примере метод ExampleMethod вызывается трижды. В первых двух вызовах метода используются позиционные аргументы. В первом пропускаются оба необязательных аргумента, а во втором — последний. Третий вызов метода предоставляет позиционный аргумент для обязательного параметра, но использует именованный аргумент для передачи значения в параметр description , в то время как аргумент optionalInt опускается.

  • Метод, индексатор или конструктор является кандидатом на выполнение, если каждый из его параметров необязателен либо по имени или позиции соответствует одному и тому же аргументу в операторе вызова, и этот аргумент можно преобразовать в тип параметра.
  • Если найдено более одного кандидата, правила разрешения перегрузки для предпочтительных преобразований применяются к аргументам, указанным явно. Опущенные аргументы для необязательных параметров игнорируются.
  • Если два кандидата определяются как равно подходящие, предпочтение отдается кандидату без необязательных параметров, аргументы которых в вызове были опущены. Это — последовательность определения приоритетов в разрешении перегрузки для кандидатов с меньшим числом параметров.

Возвращаемые значения

Методы могут возвращать значение вызывающему объекту. Если тип возвращаемого значения (указанный перед именем метода) не void указан, метод может вернуть значение с помощью ключевого return слова. Инструкция с ключевым словом return , за которым следует переменная, константа или выражение, соответствующие типу возврата, будут возвращать это значение объекту, вызвавшему метод. Методы с типом возврата, отличным от void, должны использовать ключевое слово return для возврата значения. Ключевое слове return также останавливает выполнение метода.

Если тип возврата — void , инструкцию return без значения по-прежнему можно использовать для завершения выполнения метода. Без ключевого слова return этот метод будет останавливать выполнение при достижении конца блока кода.

Например, в следующих двух методах ключевое слово return используется для возврата целочисленных значений.

Чтобы использовать значение, возвращаемое из метода, вызывающий метод может применять сам вызов метода везде, где будет достаточно значения того же типа. Можно также назначить возвращаемое значение переменной. Например, следующие два примера кода достигают одной и той же цели.

Использование локальной переменной, в данном случае result , для сохранения значения является необязательным. Это может улучшить читаемость кода или может оказаться необходимым, если нужно сохранить исходное значение аргумента для всей области метода.

После этого вызывающий объект может использовать возвращенный кортеж в коде следующего вида:

Имена могут также назначаться элементам кортежа в определении типа кортежа. В следующих примерах демонстрируется альтернативная версия метода GetPersonalInfo , в котором используются именованные элементы:

После этого предыдущий вызов метода GetPersonInfo можно изменить следующим образом:

Методы расширения

Как правило, добавлять методы в существующий тип можно двумя способами:

  • Изменение исходного кода для этого типа. Конечно, это невозможно сделать, если вы не владеете исходным кодом типа. Если при этом в поддержку метода также добавляются поля закрытых данных, это изменение становится критическим.
  • Определение нового метода в производном классе. Невозможно добавить метод таким образом, используя наследование для других типов, таких как структуры и перечисления. Кроме того, оно не позволяет "добавить" метод в запечатанный класс.

Методы расширения позволяют "добавить" метод в существующий тип, не меняя сам тип и не реализуя новый метод в наследуемом типе. Метод расширения также не должен находиться в той же сборке, что и тип, который он расширяет. Вызовите метод расширения, как будто он является определенным членом типа.

Дополнительные сведения см. в статье Методы расширения.

Асинхронные методы

С помощью функции async можно вызывать асинхронные методы, не прибегая к использованию явных обратных вызовов или ручному разделению кода между несколькими методами или лямбда-выражениями.

Если пометить метод с помощью модификатора async , можно использовать в этом методе инструкцию await . Когда элемент управления достигает await выражения в асинхронном методе, элемент управления возвращается вызывающей объекту, если ожидающая задача не завершена, а ход выполнения метода с await ключевым словом приостанавливается до завершения ожидаемой задачи. После завершения задачи можно возобновить выполнение в методе.

Асинхронный метод возвращается в вызывающий объект, когда он встречает первый ожидаемый объект, выполнение которого еще не завершено, или когда выполнение асинхронного метода доходит до конца — в зависимости от того, что происходит раньше.

В следующем примере DelayAsync представляет собой асинхронный метод с оператором return, который возвращает целое число. Поскольку это асинхронный метод, его объявление метода должно иметь тип возвращаемого Task значения . Поскольку тип возврата — Task , вычисление выражения await в DoSomethingAsync создает целое число, как показывает следующий оператор: int result = await delayTask .

Асинхронный метод не может объявлять параметры in, ref или out, но может вызывать методы, имеющие такие параметры.

Элементы, воплощающие выражение

Обычно используются определения методов, которые просто возвращаются немедленно с результатом выражения или имеют одну инструкцию в качестве тела метода. Существует сочетание синтаксиса для определения таких методов с помощью => :

Если метод возвращает void или является асинхронным, текст этого метода должен быть выражением оператора (как и при использовании лямбда-выражений). Для свойств и индексаторов они должны быть доступны только для чтения, и ключевое get слово accessor не используется.

Iterators

Итератор выполняет настраиваемую итерацию по коллекции, например по списку или массиву. Итератор использует инструкцию yield return для возврата всех элементов по одному. По достижении оператора yield return текущее расположение запоминается, чтобы вызывающий объект мог запросить следующий элемент в последовательности.

Одной из основных черт C++, которой нет в С, является концепция классов. По существу, классы - самое важное понятие в C++. Классы похожи на структуры языка С. Однако структура С определяет только данные, ассоциированные с этой структурой. Вот пример структуры С:

После того как вы объявили структуру, вы можете использовать ее в пределах вашей функции main (), как показано ниже:

Со структурой MyCircle (представляющей окружность) ассоциируются данные radius и color (радиус и цвет). Класс в C++, с другой стороны, имеет как ассоциированные с ним данные, так и функции. Данные класса называются элементами данных, а функции класса - элементами-функциями. Следовательно, в программе, которая использует классы, можно написать следующий код:

Первые два оператора присваивают значения элементам данных MyCircle radius и color; третий оператор вызывает функцию-элемент DisplayCircle() для вывода окружности MyCircle. MyCircle называется объектом класса circle. Ваша программа может объявить другой объект с именем HerCircle класса circle следующим образом:

Следующие операторы присваивают значения элементам данных HerCircle radius и color:

Затем вы можете использовать функцию-элемент DisplayCircie () для вывода окружности HerCircle:

Объявление класса

Перед тем как работать с классом, ваша программа должна его объявить (так же как перед работой со структурой mystructure вы должны были объявить ее элементы данных). В данном разделе вы познакомитесь с синтаксисом объявления класса. Вы будете и дальше практиковаться с классом circle:

Объявление класса имеет следующее строение:

Ключевое слово class показывает компилятору, что все находящееся в фигурных скобках (<>) принадлежит объявлению класса. (Не забывайте ставить точку с запятой в конце объявления.) Объявление класса содержит объявление элементов данных (например, int radius) и прототипы функций-элементов класса. В объявлении класса circle содержатся следующие элементы данных:

Объявление также содержит пять прототипов функций-элементов:

Первый и четвертый прототипы выглядят странно. Первый из них является прототипом функции конструктора:

Вы узнаете о роли конструктора позже в этом разделе, а пока запомните синтаксис, который используется в C++ для прототипа функции конструктора. Когда вы записываете прототип конструктора, вы должны следовать правилам, приведенным ниже:

  • Каждое объявление класса должно включать прототип функции конструктора.
  • Имя функции конструктора должно совпадать с именем класса, а после него должны следовать круглые скобки (). Если, например, вы объявляете класс с именем Rectangle, он должен включать объявление функции конструктора класса: Rectangle (). Следовательно, объявление класса Rectangle должно выглядеть так:

Не упоминайте никакого возвращаемого значения для функции конструктора. (Функция конструктора должна иметь тип void, но не нужно это указывать.)

Функция конструктора должна располагаться под ключевым словом

Функция конструктора всегда возвращает значение типа void (несмотря на то, что вы не указали его в прототипе). Как вы вскоре увидите, функция конструктора обычно имеет один или большее число параметров.

Функция деструктора

Функция деструктора записывается в объявлении класса следующим образом:

Обратите внимание на символ тильды (~), который предшествует прототипу функции деструктора. (На большинстве клавиатур вы можете найти символ тильды слева от клавиши 1.) При записи прототипа функции деструктора соблюдайте следующие правила:

Имя функции деструктора должно совпадать с именем класса и ему должен предшествовать символ ~. Если, например, вы объявляете класс с именем Rectangle, именем функции деструктора должно быть ~Rectangle. Следовательно, объявление класса Rectangle должно выглядеть следующим образом:

Не указывайте никакого возвращаемого значения для функции деструктора. (Функция деструктора должна иметь тип void, но не нужно это указывать.)

Функция деструктора не имеет никаких параметров.

Ключевые слова public и private

Прототипы функций и объявления элементов данных включаются в объявлении класса в разделы public (открытый) или private (закрытый). Ключевые слова public и private говорят компилятору о доступности элементов-функций и данных. Например, функция SetRadius() определена в разделе public, и это означает, что любая функция программы может вызвать функцию SetRadius(). Функция CalculateArea() определена в разделе private, и эту функцию можно вызвать только в коде функций-элементов класса Circle

Аналогично, поскольку элемент данных radius объявлен в разделе private, прямой доступ к нему (для установки или чтения его значения) возможен только в коде функций-элементов класса Circle. Если бы вы объявили элемент данных radius в разделе public, то любая функция программы имела бы доступ (для чтения и присваивания) к элементу данных radius.

Перегруженные функции

В C++ (но не в С) вы можете использовать одно и то же имя для нескольких функций. Например, вы можете объявить две функции с именем SetRadius() в объявлении класса CCircle. Такие функции называются перегруженными функциями.

Конструкторы классов. Java JDK 1.5 - 1

Конструктор имеется в любом классе. Даже если вы его не написали, компилятор Java сам создаст конструктор по умолчанию (default constructor). Этот конструктор пустой и не делает ничего, кроме вызова конструктора суперкласса. Т.е. если написать: то это эквивалентно написанию: В данном случае явно класса предка не указано, а по умолчанию все классы Java наследуют класс Object поэтому вызывается конструктор класса Object . Если в классе определен конструктор с параметрами, а перегруженного конструктора без параметров нет, то вызов конструктора без параметров является ошибкой. Тем не менее, в Java, начиная с версии 1.5, можно использовать конструкторы с аргументами переменной длины. И если есть конструктор, имеющий аргумент переменной длины, то вызов конструктора по умолчанию ошибкой не будет. Не будет потому, что аргумент переменной длины может быть пустым. Например, следующий пример не будет компилироваться, однако если раскомментарить конструктор с аргументом переменной длины, то компиляция и запуск пройдут успешно и в результате работы строки кода DefaultDemo dd = new DefaultDemo() ; вызовется конструктор DefaultDemo(int . v) . Естественно, что в данном случае необходимо пользоваться JSDK 1.5. Файл DefaultDemo.java Результат вывода программы при раскомментаренном конструкторе: Однако, в распространенном случае, когда в классе вообще не определено ни одного конструктора, вызов конструктора по умолчанию (без параметров) будет обязательным явлением, поскольку подстановка конструктора по умолчанию происходит автоматически.

Создание объекта и конструкторы

  • Ищется класс объекта среди уже используемых в программе классов. Если его нет, то он ищется во всех доступных программе каталогах и библиотеках. После обнаружения класса в каталоге или библиотеке выполняется создание, и инициализация статических полей класса. Т.е. для каждого класса статические поля инициализируются только один раз.
  • Выделяется память под объект.
  • Выполняется инициализация полей класса.
  • Отрабатывает конструктор класса.
  • Формируется ссылка на созданный и инициализированный объект. Эта ссылка и является значением выражения, создающего объект. Объект может быть создан и с помощью вызова метода newInstance() класса java.lang.Class . В этом случае используется конструктор без списка параметров.

Перегрузка конструкторов

Конструкторы одного класса могут иметь одинаковое имя и различную сигнатуру. Такое свойство называется совмещением или перегрузкой(overloading). Если класс имеет несколько конструкторов, то присутствует перегрузка конструкторов.

Параметризированные конструкторы

Сигнатура конструктора – это количество и типы параметров, а также последовательность их типов в списке параметров конструктора. Тип возвращаемого результата не учитывается. Конструктор не возвращает никаких параметров. Это положение объясняет в некотором смысле, как Java различает перегруженные конструкторы или методы. Java различает перегруженные методы не по возвращаемому типу, а по числу, типам и последовательности типов входных параметров. Конструктор не может возвращать даже тип void , иначе он превратится в обычный метод, даже не смотря на сходство с именем класса. Следующий пример демонстрирует это. Файл VoidDemo.java В результате программа выведет: Это лишний раз доказывает, что конструктором является метод без возвращаемых параметров. Тем не менее, для конструктора можно задать один из трех модификаторов public , private или protected . И пример теперь будет выглядеть следующим образом: Файл VoidDemo2.java В конструкторе разрешается записывать оператор return , но только пустой, без всякого возвращаемого значения. Файл ReturnDemo.java

Конструкторы, параметризированные аргументами переменной длины

В Java SDK 1.5 появился долгожданный инструмент – аргументы переменной длины для конструкторов и методов(variable-length arguments). До этого переменное количество документов обрабатывалось двумя неудобными способами. Первый из них был рассчитан на то, что максимальное число аргументов ограничено небольшим количеством и заранее известно. В таком случае можно было создавать перегружаемые версии метода, по одной на каждый вариант списка передаваемых в метод аргументов. Второй способ рассчитан на неизвестное заранее и большое количество аргументов. В этом случае аргументы помещались в массив, и этот массив передавался методу. Аргументы переменной длины чаще всего задействованы в последующих манипуляциях с инициализациями переменных. Отсутствие некоторых из ожидаемых аргументов конструктора или метода удобно заменять значениями по умолчанию. Аргумент переменной длины есть массив, и обрабатывается как массив. Например, конструктор для класса Checking с переменным числом аргументов будет выглядеть так: Символьная комбинация . сообщает компилятору о том, что будет использоваться переменное число аргументов, и что эти аргументы будут храниться в массиве, значение ссылки на который содержится в переменной n. Конструктор может вызываться с разным числом аргументов, включая их полное отсутствие. Аргументы автоматически помещаются в массив и передаются через n. В случае отсутствия аргументов длина массива равна 0. В список параметров наряду с аргументами переменной длины могут быть включены и обязательные параметры. В этом случае параметр, содержащий переменное число аргументов должен обязательно быть последним в списке параметров. Например: Вполне очевидное ограничение касается количества параметров с переменной длиной. В списке параметров должен быть только один параметр переменной длины. При наличии двух параметров переменной длины компилятору невозможно определить, где заканчивается один параметр и начинается другой. Например: Файл Checking.java Например, есть аппаратура, способная распознавать номера автомобилей и запоминать номера квадратов местности, где побывал каждый из автомобилей за день. Необходимо из общей массы зафиксированных автомобилей отобрать те, которые в течение дня побывали в двух заданных квадратах, скажем 22 и 15, согласно карте местности. Вполне естественно, что автомобиль может в течение дня побывать во многих квадратах, а может только в одном. Очевидно, что количество посещенных квадратов ограничено физической скоростью автомобиля. Составим небольшую программу, где конструктор класса будет принимать в качестве аргументов номер автомобиля как обязательный параметр и номера посещенных квадратов местности, число которых может быть переменным. Конструктор будет проверять, не появился ли автомобиль в двух квадратах, если появился, то вывести его номер на экран.

Передача параметров в конструктор

  • основные типы (примитивы);
  • ссылки на объекты.
  • конструктор не может менять значения входных параметров основных (примитивных) типов;
  • конструктор не может изменять ссылки входных параметров;
  • конструктор не может переназначать ссылки входных параметров на новые объекты.
  • изменять состояние объекта, передаваемого в качестве входного параметра.

Конструкторы и блоки инициализации, последовательность действий при вызове конструктора

  1. Все поля данных инициализируются своими значениями, предусмотренными по умолчанию (0, false или null).
  2. Инициализаторы всех полей и блоки инициализации выполняются в порядке их перечисления в объявлении класса.
  3. Если в первой строке конструктора вызывается другой конструктор, то выполняется вызванный конструктор.
  4. Выполняется тело конструктора.
  • присвоить значение в объявлении;
  • присвоить значения в блоке инициализации;
  • задать его значение в конструкторе.

Ключевое слово this в конструкторах

Конструкторы используют this чтобы сослаться на другой конструктор в этом же классе, но с другим списком параметров. Если конструктор использует ключевое слово this , то оно должно быть в первой строке, игнорирование этого правила приведет к ошибке компилятора. Например: Файл ThisDemo.java Результат вывода программы: В данном примере имеется два конструктора. Первый получает строку-аргумент. Второй не получает никаких аргументов, он просто вызывает первый конструктор используя имя "John" по-умолчанию. Таким образом, можно с помощью конструкторов инициализировать значения полей явно и по умолчанию, что часто необходимо в программах.

Ключевое слово super в конструкторах

Конструкторы используют super , чтобы вызвать конструктор суперкласса. Если конструктор использует super , то этот вызов должен быть в первой строке, иначе компилятор выдаст ошибку. Ниже приведен пример: Файл SuperClassDemo.java В этом простом примере конструктор Child() содержит вызов super() , который создает экземпляр класса SuperClassDemo , в дополнение к классу Child . Так как super должен быть первым оператором, выполняемым в конструкторе подкласса, этот порядок всегда одинаков и не зависит от того, используется ли super() . Если он не используется, то сначала будет выполнен конструктор по умолчанию (без параметров) каждого суперкласса, начиная с базового класса. Следующая программа демонстрирует, когда выполняются конструкторы. Файл Call.java Вывод этой программы: Конструкторы вызываются в порядке подчиненности классов. В этом есть определенный смысл. Поскольку суперкласс не имеет никакого знания о каком-либо подклассе, то любая инициализация, которую ему нужно выполнить, является отдельной. По возможности она должна предшествовать любой инициализации, выполняемой подклассом. Поэтому-то она и должна выполняться первой.

Настраиваемые конструкторы

Механизм идентификации типа во время выполнения является одним из мощных базовых принципов языка Java, который реализует полиморфизм. Однако такой механизм не страхует разработчика от несовместимого приведения типов в ряде случаев. Самый частый случай – манипулирование группой объектов, различные типы которых заранее неизвестны и определяются во время выполнения. Поскольку ошибки, связанные с несовместимостью типов могут проявиться только на этапе выполнения, то это затрудняет их поиск и ликвидацию. Введение настраиваемых типов в Java 2 5.0 частично отодвигает возникновение подобных ошибок с этапа выполнения на этап компиляции и обеспечивает недостающую типовую безопасность. Отпадает необходимость в явном приведении типов при переходе от типа Object к конкретному типу. Следует иметь ввиду, что средства настройки типов работают только с объектами и не распространяются на примитивные типы данных, которые лежат вне дерева наследования классов. Благодаря настраиваемым типам все приведения выполняются автоматически и скрыто. Это позволяет обезопасить от несоответствия типов и гораздо чаще повторно использовать код. Настраиваемые типы можно использовать в конструкторах. Конструкторы могут быть настраиваемыми, даже если их класс не является настраиваемым типом. Например: Поскольку конструктор GenConstructor задает параметр настраиваемого типа, который должен быть производным классом от класса Number , его можно вызвать с любы

Читайте также: