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{}
"В"
Тогда я придумал это:
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
). Но это дает неоднозначную ссылочную ошибку.
Как это на самом деле выбирает конструктор? Я прочитал документы, но, честно говоря, я не мог понять, как это определяет специфику. Я надеюсь, что описание точно, почему это не может определить, что E
более конкретно, чем A
,
3 ответа
Оно не основано на количестве типов, которые могут быть преобразованы в тип параметра, а в том, является ли любое значение, допустимое для одной перегрузки, допустимым для другого из-за неявных преобразований.
Например, есть неявное преобразование из 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)
.)
Следующее утверждение JSL§15.12.2.5 отвечает, что
Неформальная интуиция заключается в том, что один метод более специфичен, чем другой, если любой вызов, обработанный первым методом, может быть передан другому без ошибки во время компиляции.
Случай 1
- Вы можете передать что-нибудь в конструктор, который принимает
Object
пока мы не можем передать ничего, кромеMap
в первом конструкторе. Так что я прохожуMap
конструктор может быть обработанObject
конструктор и вот почемуTest(Map map)
становится более конкретным.
Дело 2
- поскольку
B
продолжаетсяA
, ВотTest(B b)
Конструктор становится более конкретным. Как мы можем пройтиB
вTest(A a)
благодаря наследству.
Дело 3
- В этом случае нет прямого преобразования, чтобы изобразить более конкретный метод, и это приводит к неоднозначности.
Такое поведение объясняется тем, что E не более специфичен, чем A, поскольку они принадлежат разным иерархиям, и их нельзя сравнивать. Таким образом, когда вы передаете нуль, Java не может знать, какую иерархию использовать.