Сигнатура метода java что это
Completing the CAPTCHA proves you are a human and gives you temporary access to the web property.
What can I do to prevent this in the future?
If you are on a personal connection, like at home, you can run an anti-virus scan on your device to make sure it is not infected with malware.
If you are at an office or shared network, you can ask the network administrator to run a scan across the network looking for misconfigured or infected devices.
Another way to prevent getting this page in the future is to use Privacy Pass. You may need to download version 2.0 now from the Chrome Web Store.
Cloudflare Ray ID: 71aab737ed439b74 • Your IP : 82.102.23.104 • Performance & security by Cloudflare
Перегрузка, которая запрещена, или bridge-методы в Java

В большинстве моих собеседований на технические позиции есть задача, в которой кандидату необходимо реализовать 2 очень похожих интерфейса в одном классе:
Реализуйте оба интерфейса одним классом, если это возможно. Объясните, почему это возможно или нет.
От переводчика: Эта статья не призывает вас задавать такие же вопросы на интервью. Но если вы хотите быть во всеоружии, когда этот вопрос зададут вам, то добро пожаловать под кат.
Иногда соискатели, которые не очень уверены в ответе, предпочитают решить вместо этой задачу со следующим условием (позже я в любом случае прошу ее решить):
И правда, вторая задача кажется намного проще, и большинство кандидатов отвечают, что включение обоих методов в один и тот же класс невозможно, потому что сигнатуры S.m(int) и V.m(int) одинаковы, в то время как тип возвращаемого значения — разный. И это абсолютно верно.
Однако иногда я задаю другой вопрос, связанный с этой темой:
Как вы думаете, есть ли смысл в том, чтобы допускать реализацию методов с одинаковой сигнатурой, но разными типами в одном классе? Например, в неком гипотетическом языке на базе JVM или хотя бы на уровне JVM?
Это вопрос, ответ на который неоднозначен. Но, не смотря на то, что я не ожидаю ответа на него, правильный ответ существует. Ответить на него смог бы человек, который часто имеет дело с API рефлексии, манипулирует байт-кодом или знаком со спецификацией JVM.
Сигнатура метода Java и дескриптор метода JVM
Сигнатура метода Java (т.е. название метода и типы параметров) применяется только Java компилятором во время компиляции. В свою очередь, JVM разделяет методы в классе с помощью неквалифицированного имени метода (то есть просто имени метода) и дескриптора метода, то есть перечня параметров дескриптора и одного return-дескриптора.
Например, если мы хотим вызвать метод String m(int i) непосредственно на классе foo.Bar , необходим следующий байт-код:
а для void m(int i) следующий:
Таким образом, JVM вполне комфортно себя чувствует с String m(int i) и void m(int i) в одном классе. Все, что нужно, — это сгенерировать соответствующий байт-код.
Кунг-фу с байт-кодом
У нас есть интерфейсы S и V, теперь мы создадим класс SV, который включает оба интерфейса. В Java, если бы это было разрешено, это должно выглядеть так:
Чтобы сгенерировать байт-код, мы используем Objectweb ASM library, достаточно низкоуровневую библиотеку, чтобы получить представление о JVM байт-коде.
Полный исходный код залит на GitHub, здесь же я приведу и поясню только наиболее важные фрагменты.
Начнем с создания ClassWriter для генерации байт-кода.
Теперь мы объявим класс, в который входят интерфейсы S и V.
Хотя наш референсный псевдо-java код для SV не имеет конструкторов, нам все равно нужно генерировать код для него. Если мы не описываем конструкторы на Java, компилятор неявно генерирует пустой конструктор.
В теле методов мы начнем с получения поля System.out с типом java.io.PrintStream и добавления его в стек операндов. Затем загружаем константу ( String или void ) в стек и вызываем команду println в полученной переменной out со строковой константой в качестве аргумента.
Наконец, для String m(int i) добавляем в стек константу ссылочного типа со значением null и используем оператор return соответствующего типа, то есть ARETURN , чтобы вернуть значение в инициатор вызова метода. Для void m(int i) необходимо использовать нетипизированный RETURN только для того, чтобы вернуться к инициатору вызова метода без возврата значения. Чтобы убедиться в правильности байт-кода (что я делаю постоянно, многократно исправляя ошибки), мы записываем сгенерированный класс на диск.
и используем jad (декомпилятор Java), чтобы перевести байт-код обратно в исходный код на Java:
Использование сгенерированного класса
Успешная декомпиляция jad по сути ничего нам не гарантирует. Утилита jad оповещает только об основных проблемах в байт-коде, от таких, как размер фрейма, до несоответствия локальных переменных или отсутствующего оператора возврата.
Чтобы использовать сгенерированный класс во время исполнения, нам необходимо каким-то образом загрузить его в JVM и затем создать его экземпляр.
Давайте реализуем собственный AsmClassLoader . Это просто удобная обёртка для ClassLoader.defineClass :
Теперь используем этот class loader и создадим экземпляр класса:
Поскольку наш класс сгенерирован во время исполнения, мы не можем использовать его в исходном коде. Зато мы можем привести его тип к реализованным интерфейсам. А вызов без рефлексии можно осуществить так:
При выполнении кода мы получим следующий вывод:
Кому-то такой вывод покажется неожиданным: мы обращаемся к одному и тому же (с точки зрения Java) методу в классе, но результаты различаются в зависимости от интерфейса, к которому мы привели объект. Сногсшибательно, правда?
Все станет понятно, если принять во внимание лежащий в основе байт-код. Для нашего вызова компилятор генерирует инструкцию INVOKEINTERFACE, и дескриптор метода исходит не из класса, а из интерфейса.
Таким образом, при первом вызове мы получим:
Объект, на котором мы выполнили вызов, можно получить из стека. Это и есть могущество полиморфизма, присущее Java.
Имя ему — bridge-метод
Кто-то спросит: «Так в чем смысл всего этого? Пригодится ли это когда-нибудь?»
Смысл в том, что мы используем всё то же самое (неявно) при написании обычного Java кода. Например, ковариантные возвращаемые типы, дженерики и доступ к private-полям из внутренних классов реализуются с помощью такой же магии байт-кода.
Взгляните на такой интерфейс:
и его реализацию с возвратом ковариантного типа:
Теперь подумаем над этим кодом:
Для iz.getZero() компилятор вызова будет генерировать INVOKEVIRTUAL с методом дескриптора ()Ljava/lang/Integer; , в то время как для zp.getZero() он сгенерирует INVOKEINTERFACE с дескриптором метода ()Ljava/lang/Number; . Мы уже знаем, что JVM выполняет диспетчеризацию вызова объекта с помощью имени и дескриптора метода. Так как дескрипторы разные, эти 2 вызова не могут направляться в один и тот же метод в экземпляре IntegerZero .
По сути, компилятор генерирует дополнительный метод, выполняющий роль моста между реальным методом, указанным в классе, и методом, используемым при вызове через интерфейс. Отсюда название — bridge-метод. Если бы в Java такое было возможно, конечный код выглядел бы так:
Послесловие
Язык программирования Java и виртуальная машина Java — это не одно и то же: хотя они имеют в названии общее слово и Java является основным языком для JVM, их возможности и ограничения далеко не всегда одинаковы. Знание JVM помогает лучше понимать Java или любой другой основанный на JVM язык, но, с другой стороны, знание Java и его истории помогают понять определенные решения в дизайне JVM.
От переводчика
Вопросы совместимости рано или поздно начинают волновать любого разработчика. В исходной статье затронут важный вопрос о неявном поведении компилятора Java и влиянии его магии на приложения, который нас как разработчиков фреймворка CUBA Platform волнует довольно сильно, — это напрямую влияет на совместимость библиотек. Совсем недавно мы рассказывали о совместимости в реальных приложениях на JUG в Екатеринбурге в докладе «API на переправе не меняют — как построить стабильный API», видео встречи можно найти по ссылке.
Методы в Java
Узнайте все о методах в Java, от базового синтаксиса метода до перегрузки, а также о том, как вызывать методы.
- Автор записи
1. введение
В Java методы – это то, где мы определяем бизнес-логику приложения. Они определяют взаимодействие между данными, заключенными в объекте.
В этом уроке мы рассмотрим синтаксис методов Java, определение сигнатуры метода, а также то, как вызывать и перегружать методы .
2. Синтаксис метода
Во-первых, метод состоит из шести частей:
- Модификатор доступа: дополнительно мы можем указать, из какого кода можно получить доступ к методу
- Тип возвращаемого значения: тип значения, возвращаемого методом, если таковой имеется
- Идентификатор метода: имя, которое мы даем методу
- Список параметров: необязательный разделенный запятыми список входных данных для метода
- Список исключений: необязательный список исключений, которые может выдавать метод
- Тело: определение логики (может быть пустым)
Давайте рассмотрим пример:
Давайте подробнее рассмотрим каждую из этих шести частей метода Java.
2.1. Модификатор доступа
Модификатор access позволяет нам указать, какие объекты могут иметь доступ к методу. Существует четыре возможных модификатора доступа: public, protected, private и default (также называемый package-private ).
Метод также может включать ключевое слово static | до или после модификатора доступа. Это означает, что метод принадлежит классу, а не экземплярам, и поэтому мы можем вызвать метод, не создавая экземпляр класса. Методы без ключевого слова static известны как методы экземпляра и могут вызываться только на экземпляре класса.
Что касается производительности, статический метод будет загружен в память только один раз – во время загрузки класса – и, таким образом, более эффективен с точки зрения памяти.
2.2. Тип возврата
Методы могут возвращать данные в код, из которого они были вызваны. Метод может возвращать примитивное значение или ссылку на объект, или он не может возвращать ничего , если мы используем ключевое слово void в качестве возвращаемого типа.
Давайте рассмотрим пример метода void :
Если мы объявляем тип возвращаемого значения, то мы должны указать оператор return в теле метода. Как только оператор return будет выполнен, выполнение тела метода будет завершено, и если будет больше операторов, они не будут обработаны.
С другой стороны, метод void не возвращает никакого значения и, следовательно, не имеет оператора return .
2.3. Идентификатор метода
Идентификатор метода-это имя, которое мы присваиваем спецификации метода. Рекомендуется использовать информативное и описательное название. Стоит отметить, что идентификатор метода может содержать не более 65536 символов (хотя это длинное имя).
2.4. Список параметров
Мы можем указать входные значения для метода в его списке параметров, который заключен в круглые скобки. Метод может иметь от 0 до 255 параметров, разделенных запятыми. Параметром может быть объект , примитив или перечисление . Мы можем использовать аннотации Java на уровне параметров метода (например, аннотацию Spring @RequestParam ).
2.5. Список исключений
Мы можем указать, какие исключения генерируются методом, используя предложение throws . В случае проверенного исключения/| либо мы должны заключить код в предложение try-catch , либо мы должны предоставить предложение throws в сигнатуре метода.
Итак, давайте рассмотрим более сложный вариант нашего предыдущего метода, который вызывает проверенное исключение:
2.6. Тело метода
Последняя часть метода Java-это тело метода, которое содержит логику, которую мы хотим выполнить. В теле метода мы можем написать столько строк кода, сколько захотим, или вообще ничего в случае статических методов. Если наш метод объявляет тип возвращаемого значения, то тело метода должно содержать оператор return.
3. Сигнатура метода
Согласно его определению, сигнатура метода состоит только из двух компонентов — имя метода и список параметров .
Итак, давайте напишем простой метод:
Сигнатура этого метода – getName(String FirstName, String LastName) .
Идентификатор метода может быть любым идентификатором. Однако, если мы следуем общепринятым соглашениям о кодировании Java, идентификатор метода должен быть глаголом в нижнем регистре, за которым могут следовать прилагательные и/или существительные.
4. Вызов метода
Теперь давайте рассмотрим как вызвать метод в Java . Следуя предыдущему примеру, предположим, что эти методы заключены в класс Java , называемый Имя человека :
Поскольку наш метод getName является методом экземпляра, а не статическим методом , для вызова метода getName нам необходимо создать экземпляр класса | PersonName :
Как мы видим, мы используем созданный объект для вызова метода getName .
Наконец, давайте посмотрим на как вызвать статический метод . В случае статического метода нам не нужен экземпляр класса для выполнения вызова. Вместо этого мы вызываем метод с его именем, префиксом имени класса.
Давайте продемонстрируем, используя вариант предыдущего примера:
В этом случае вызов метода является:
5. Способ Перегрузки
Java позволяет нам иметь два или более методов с одинаковым идентификатором, но разным списком параметров — разные сигнатуры методов . В этом случае мы говорим, что метод перегружен . Давайте рассмотрим пример:
Перегрузка метода полезна в случаях, подобных приведенному в примере, когда у нас может быть метод, реализующий упрощенную версию той же функциональности.
Наконец, хорошая привычка к дизайну заключается в том, чтобы гарантировать, что перегруженные методы ведут себя аналогичным образом. В противном случае код будет сбивать с толку, если метод с тем же идентификатором будет вести себя по-другому.
6. Заключение
В этом уроке мы рассмотрели части синтаксиса Java, используемые при указании метода в Java.
В частности, мы рассмотрели модификатор доступа, тип возвращаемого значения, идентификатор метода, список параметров, список исключений и тело метода. Затем мы увидели определение сигнатуры метода, как вызвать метод и как перегрузить метод.