Как Java выбирает правильный метод для вызова при перегрузке [дубликат]

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

См. также: A хороший список лучших практик

Я бы добавил, очень важно, хорошо использовать модификатор final. Использование "окончательной" модификатор, когда это применимо в Java

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

63
задан edmz 10 September 2015 в 10:36
поделиться

4 ответа

Методы с varargs (...) имеют самый низкий приоритет, когда компилятор определяет, какой перегруженный метод выбрать. Поэтому TestOverload(int i) выбирается над TestOverload(char... c), когда вы вызываете TestOverload с одним параметром char 'a', так как char можно автоматически повысить до int.

JLS 15.12.2 :

  1. Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешения преобразования бокса или распаковки или использования метода переменной arity призывание. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до второй фазы. Это гарантирует, что любые вызовы, которые были действительны на языке программирования Java до Java SE 5.0, не считаются неоднозначными в результате внедрения методов переменной arity, неявного бокса и / или распаковки. Однако объявление метода переменной arity (§8.4.1) может изменить метод, выбранный для выражения вызова метода данного метода, поскольку метод переменной arity рассматривается как метод фиксированной arity в первой фазе. Например, объявление m (Object ...) в классе, который уже объявляет m (Object), приводит к тому, что m (Object) больше не выбирается для некоторых выражений вызова (таких как m (null)), как m (Object []) ) более конкретно.
  2. Вторая фаза (§15.12.2.3) выполняет разрешение перегрузки при разрешении бокса и распаковки, но все же исключает использование вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до третьей фазы. Это гарантирует, что метод никогда не выбирается с помощью вызова метода переменной arity, если он применим посредством вызова метода фиксированной arity.
  3. Третья фаза (§15.12.2.4) позволяет перегрузке сочетаться с различными способами arity, бокс , и unboxing.

EDIT:

Вы хотите заставить компилятор вызвать конструктор TestOverload(char... c), вы можете перейти к вызову конструктора a char[]:

new TestOverload (new char[] {'a'});
81
ответ дан Eran 19 August 2018 в 07:30
поделиться
  • 1
    О, хотя либо он назовет правильный метод, либо даст ошибку двусмысленности. Есть ли способ вызвать правильный метод без изменения подписи метода? – Amit Kumar Gupta 9 September 2015 в 05:29
  • 2
    @AmitGupta Вы можете передать массив символов с одним символом: new TestOverload (new char[] {'a'}); – Eran 9 September 2015 в 05:31
  • 3
    new TestOverload(new char[] {'a'}) – Erwin Bolwidt 9 September 2015 в 05:32
  • 4
    @Eran Для полноты вам, вероятно, следует добавить объяснение того, как явным образом вызывать метод varargs для вашего ответа. :-) – megaflop 9 September 2015 в 15:18
  • 5
    Если вы добавите public TestOverload(char c){ this(new char[]{ c }); }, вызов конструктора станет более приятным: new TestOverload('a'), как захотел OP. – Justin 10 September 2015 в 05:41

Я взял код из этой ссылки и изменил некоторые его части:

    public static void main(String[] args) {
    Byte i = 5;
    byte k = 5;
    aMethod(i, k);
}

//method 1
static void aMethod(byte i, Byte k) {
    System.out.println("Inside 1");
}

//method 2
static void aMethod(byte i, int k) {
    System.out.println("Inside 2");
}

//method 3
static void aMethod(Byte i, Byte k) {
    System.out.println("Inside 3 ");
}

//method 4
static void aMethod(Byte  i, Byte ... k) {
    System.out.println("Inside 4 ");
}

Компилятор дает ошибку (метод неоднозначен для типа Overloading) для методов 1, 2 и 3, но не 4 (почему?)

Ответ заключается в механизме, который java использует для сопоставления вызовов методов методам. Механизм выполняется в три этапа, в каждой фазе, если он находит метод совпадения:

+ фаза первая: используйте расширение, чтобы найти метод сопоставления (не найдено совпадающих методов)

+ второй этап: (также) использовать бокс / unboxing для поиска метода сопоставления (метод 1,2 и 3)

+ фаза три: (также) использовать var args (метод 4 соответствует!) [/ ​​g6]

0
ответ дан Community 19 August 2018 в 07:30
поделиться

Да, это ожидаемое поведение. Приоритет для вызова метода выглядит следующим образом:

  1. Widending
  2. Бокс
  3. Varargs

Ниже выдержки из Java docs , связанных с тем же: -

Процесс определения применимости начинается с определения потенциально применимых методов (§15.12.2.1).

Остальная часть процесса разделена на три этапа, чтобы обеспечить совместимость с версиями языка программирования Java до Java SE 5.0. Фазы:

Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешения преобразования бокса или распаковки или использования вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до второй фазы.

Это гарантирует, что любые вызовы, которые были действительны на языке программирования Java до Java SE 5.0, не считаются неоднозначными в результате внедрение методов переменной arity, неявного бокса и / или распаковки. Однако объявление метода переменной arity (§8.4.1) может изменить метод, выбранный для выражения вызова метода данного метода, поскольку метод переменной arity рассматривается как метод фиксированной arity в первой фазе. Например, объявление m (Object ...) в классе, который уже объявляет m (Object), приводит к тому, что m (Object) больше не выбирается для некоторых выражений вызова (таких как m (null)), как m (Object []) ) более конкретно.

Вторая фаза (§15.12.2.3) выполняет разрешение перегрузки при разрешении бокса и распаковки, но все же исключает использование вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до третьей фазы.

Это гарантирует, что метод никогда не выбирается при вызове метода переменной arity, если он применим посредством вызова метода фиксированной arity.

Третий этап (§15.12.2.4) позволяет комбинировать перегрузку с использованием методов переменной arity, бокса и распаковки.

36
ответ дан Panther 19 August 2018 в 07:30
поделиться
  • 1
    Извините, я начал редактировать этот вопрос, но потом понял это за моей способностью :-). Таким образом, я ниспослал и сохранил то, что когда-либо исправил. Изначально я, хотя только вопрос должен быть исправлен. Но я не могу отформатировать вопрос. – Panther 30 December 2016 в 11:29

Твердые советы от Джошуа Блоха (Эффективная Java, 2-е изд.):

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

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

Ваши строки кода нарушают это правило и открывают дверь для ошибок:

public TestOverload(int i){System.out.println("Int");}
public TestOverload(char... c){System.out.println("char");}

A char является взаимозависимым с int, поэтому единственный способ предсказать, что произойдет с вызовами, - это перейти к Спецификации языка Java и прочитать несколько тайные правила о том, как разрешаются перегрузки.

К счастью, эта ситуация не должна нуждаться в исследовании JLS. Если у вас есть аргументы, которые не сильно отличаются друг от друга, возможно, лучшим вариантом является , а не перегрузка . Дайте методам разные имена, чтобы не было возможности для ошибки или путаницы со стороны любого, кому может понадобиться поддерживать код.

Время - деньги.

13
ответ дан scottb 19 August 2018 в 07:30
поделиться
  • 1
    Я думаю, что перегрузка с аналогичными типами приемлема, если поведение перегрузок очень похоже. например int max(int x, int y) и long max(long x, long y) - отлично. – CodesInChaos 9 September 2015 в 10:15
  • 2
    Только проблема в том, что вы перегружали конструкторы, а не методы. Вы не можете выбрать другое имя. Как это было в моем случае. Однако я изменил свой код, чтобы он использовал этот полиморфизм во время выполнения, чтобы определить, какой метод следует вызывать. :) – Amit Kumar Gupta 9 September 2015 в 13:53
  • 3
    Чтобы усилить ключевой момент, какой метод мог полезно взять char или int и сделать что-то своеобразное в каждом случае? Я действительно не могу думать об этом. Я также согласен с тем, что «лучший вариант - не перегружать». Хороший ответ на неправильную работу. – msw 10 September 2015 в 02:26
  • 4
    Я создал свой собственный примитивный список, где пользователь может передать размер списка или просто char ... для создания его объекта. – Amit Kumar Gupta 13 September 2015 в 05:02
Другие вопросы по тегам:

Похожие вопросы: