2016-08-16 3 views
36

В соответствии с this question Java выберет «наиболее конкретную» опцию при попытке выбора между неоднозначными перегруженными конструкторами. В этом примере:Java: выбор между перегруженными конструкторами

public class Test{ 
    private Test(Map map){ 
     System.out.println("Map"); 
    } 
    private Test(Object o){ 
     System.out.println("Object"); 
    } 
    public static void main(String[] args){ 
     new Test(null); 
    } 
} 

он напечатает

"Карта"

Однако, я пытался выяснить точно, что "наиболее специфичный" означает. Я предполагал, что это означает «наименее двусмысленный», поскольку «может относиться к наименее возможным типам». В этом контексте Object может быть любым, что не является примитивным, тогда как Map может быть только Map или ? extends Map. В принципе, я предположил, что какой бы класс был ближе к листу дерева наследования. Это работает, когда один класс является подклассом другого:

public class Test{ 
    private Test(A a){ 
     System.out.println("A"); 
    } 
    private Test(B b){ 
     System.out.println("B"); 
    } 
    public static void main(String[] args){ 
     new Test(null); 
    } 
} 

class A{} 

class B extends A{} 

"B"

Тогда я пришел с этим:

public class Test{ 
    private Test(A a){ 
     System.out.println("A"); 
    } 
    private Test(E e){ 
     System.out.println("E"); 
    } 
    public static void main(String[] args){ 
     new Test(null); 
    } 
} 

class A{} 

class B extends A{} 

class C{} 

class D extends C{} 

class E extends D{} 

Я думаю, он должен печатать E, так как E может относиться только к одному известному типу, тогда как A может относиться к двум (A и B). Но это дает неоднозначную ошибку сравнения.

Как это на самом деле выбрать конструктор? Я прочитал the docs, но, честно говоря, я не мог полностью следить за тем, как он определяет специфику. Я надеюсь на точное описание того, почему он не может определить, что E более конкретно, чем A.

+3

ничего не может быть: ***? extends String ***, строка string окончательна –

+0

@ ΦXocę 웃 Пepeúpa ツ хорошая точка. Я исправлю это – ewok

ответ

46

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

Например, есть неявное преобразование из String в Object, но обратное неверно, поэтому String более специфично, чем Object.

Точно так же существует неявное преобразование из B в A, но обратное неверно, поэтому B более специфично, чем A.

С A и E однако, ни более специфичен, чем другие - там нет преобразования из A в E и не переход от E к A. Вот почему разрешение перегрузки выходит из строя.

Соответствующий бит JLS фактически 15.12.2.5, который включает в себя это, что может сделать его более легким для вас, чтобы понять:

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

Так что если у вас есть:

void foo(String x) 
void foo(Object x) 

каждый вызов обрабатывается foo(String) может быть обработан foo(Object), но обратное не так. (Например, вы могли бы назвать foo(new Object()) и которые не могут быть обработаны foo(String).)

+0

Эта цитата - то, что я искал. Наверное, я так далеко не читал. – ewok

+3

Вы знаете, как «сложный» (выберите любой показатель, который вы хотите)? Например. Разрешение перегрузки C resolution в NP, на самом деле, вы можете кодировать любую проблему с 3-SAT в наборе перегрузок и разрешить компилятор для вас. –

+0

@ JörgWMittag: Я совсем не знаю, я боюсь :( –

6

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

9

После заявления о JSL§15.12.2.5 ответы, которые,

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

Случай 1

  • Вы можете передать что-нибудь в конструктор, который принимает Object в то время как мы не можем ничего, кроме Map в первом конструкторе передать. Так что все, что я передаю в Map, конструктор может обрабатываться конструктором Object, и именно поэтому Test(Map map) становится конкретным.

Случай 2

  • Поскольку B расширяет A, здесь Test(B b) конструктор становится более конкретным. Как мы можем пройти B в Test(A a) благодаря inheritance.

Случай 3

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

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

  • Нет связанных вопросов^_^