Подтвердить что ты не робот

Java: выбор между перегруженными конструкторами

Per этот вопрос, 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.

4b9b3361

Ответ 1

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

Например, существует неявное преобразование от 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).)

Ответ 2

Следующее утверждение JSL§15.12.2.5 отвечает, что

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

Случай 1

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

Случай 2

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

Случай 3

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

Ответ 3

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