2015-11-01 5 views
11

выбора у меня есть два перегруженных метода: foo и barПерегрузки переменной длины массивов, метод

//Object[]... vs Integer[]... 
public static String foo(Object[]... args) { return "Object[] args"; } 
public static String foo(Integer[]... args) { return "Integer[] args";} 

//Object... vs Integer[]... 
public static String bar(Object... args) {return "Object args";} 
public static String bar(Integer[]... args) {return "Integer[] args";} 

Теперь, когда я использую их как:

Integer[] i = { 5 }; 
System.out.println(foo(i));//Object[]... vs Integer[]... 
System.out.println(bar(i));//Object... vs Integer[]... 

Я получаю

Integer[] args 
Object args 

Вот вопрос: почему у нас есть два разных выхода?
Integer[] может быть неявным образом отбрасываться как Object, так и Object[].

+2

Для тех, кто хочет играть в Шерлока [15.12.2.5. Выбор наиболее конкретного метода] (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5) – Pshemo

+0

вы должны хотя бы упомянуть предупреждение компилятора. –

+0

@Colonel, да, вы можете и да можете. Когда вы попробуете, вы получите исключение во время выполнения. –

ответ

3

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

Когда вы звоните

System.out.println(foo(i));//Object[]... vs Integer[]... 

он будет вызывать foo(Integer[]... args)

Потому что во время выполнения делегаты JVM вызов методы с Integer[][] аргументом и не методом с Object[][] парами, как указано varags , Поскольку более конкретным будет метод вызова с Integer [] [], а не Object [] [].


В последнем заявлении, когда вы звоните

System.out.println(bar(i));//Object... vs Integer[]...

он будет идти к bar(Object... args)

Опять же с помощью varags, тип PARAM будет Object [], а не Object [] []. Опять же, компилятор вызовет наиболее специфический метод, который будет иметь Object... args.

Если изменить сигнатуру метода путем удаления varags согласно следующим:

//Object... vs Integer[]... 
    public static String bar(Object args) { 

     return "Object args"; 
    } 

    public static String bar(Integer[] args) { 
     return "Integer[] args"; 
    } 

, то вы заметите, что он будет вызывать bar(Integer[] args), поскольку это более специфичны для вызова метода.

Таким образом, чтобы быть более точным в соответствии с JLS Subtyping among Array Types,

  • Если S и Т оба являются ссылочными типами, то S []> T [] тогда и только тогда S> T.
  • Объект> Объект []

Это означает, что вызов Integer [] будет сделана методом, имеющий целое число [] [], а не Object [] []. Где как вызов Integer [] будет сделан объект Object [], а не Integer [] [].

См. here для выбора наиболее конкретного метода.

3

В первом случае тип args фактически является Integer [] [], то есть ваш массив был помещен в другой массив с помощью varargs. Компилятор выбирает версию Integer [], потому что это наиболее специфический тип.

Во втором случае args == i и является Integer []. В этом случае компилятору приходилось выбирать между его завершением в новом массиве, чтобы вызвать версию Integer [] ... или просто придать Integer [] объекту []. Он выбрал второй, потому что это правило.

Мораль истории: не перегружайте методы варгара - это сбивает с толку.

+1

Можете ли вы найти бит спецификации, который говорит «это правило»? –

+0

Я мог бы ... но ОП уже доказал это со своим компилятором :) –

+0

Если вы можете это найти, я буду выше. У меня проблемы с расшифровкой. –

Смежные вопросы