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 не может знать, какую иерархию использовать.

Другие вопросы по тегам