Выбор перегрузки метода с нулем - PullRequest
4 голосов
/ 26 августа 2011

Учитывая этот код:

class Overloading
extends Object
{

static public void target(Object val, String chk) { System.out.println("Object["+val+"] :: Should be "+chk); }
static public void target(String val, String chk) { System.out.println("String["+val+"] :: Should be "+chk); }

static public void main(String[] args) {
    Object                              obj=null;

    target(null        ,"Object");
    target((Object)null,"Object");
    target(obj         ,"Object");
    }
}

вывод (неожиданно) выглядит следующим образом:

String[null] :: Should be Object
Object[null] :: Should be Object
Object[null] :: Should be Object

Проблема с первой строкой, которая, как я ожидаю, будет такой же, как и у двух других. Кроме того, я клянусь, что до недавнего времени компилятор давал мне двусмысленное предупреждение о вызове для простого вызова null. Однако компиляция и тестирование с Java 5 и 6 дают одинаковые результаты.

Это серьезная проблема для меня, так как у меня много кода, который использует этот шаблон использования перегруженного параметра «по умолчанию» различных типов для выбора типа возврата и определения требуемого преобразования / анализа. Кто-нибудь может объяснить, что здесь происходит?

Ответы [ 2 ]

6 голосов
/ 26 августа 2011

Java всегда работала одинаково: всегда выбирается «наиболее специфичная» применимая перегрузка.Поскольку String является подклассом Object, он является «более конкретным», и выбирается перегрузка String.Если бы перегрузки были, скажем, String и Integer, и вы попытались передать null, то вы бы действительно получили ошибку неоднозначности во время компиляции, так как они оба находятся на одном и том же уровне иерархии наследования.

3 голосов
/ 26 августа 2011

Помните, что литерал null имеет тип "специальный нулевой тип", а не тип Object

Распространенным заблуждением является то, что литерал null относится к типу Object, что приводит людей к мысли, что ближайшая подходящая подпись - target(Object val, String chk).

Литерал null на самом деле имеет тип "[специальный нулевой тип]" ( Спецификация языка Java (JLS) 4 ). Если бы возможно было определить такой метод, самое близкое совпадение было бы target([special null type] val, String chk).

Однако, поскольку такого метода нет (вы не могли его создать), компилятор ищет наиболее близкое совпадение с помощью подтипа ( JLS 15.12.2.2 ). Прямой супертип [специального нулевого типа] - это все ссылочные типы ( JLS 4.10.2 ) (например, String), а Object является супертипом String.


Возможно, более интуитивно понятный способ взглянуть на него - это интуитивное определение JLS для "наиболее конкретного метода" ( JLS 15.12.2.5 ):

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

Из двух методов, которым соответствует вызов target(null ,"Object"), любой вызов

void target(String val, String chk)

может обрабатываться

void target(Object val, String chk)

настолько интуитивно void target(String val, String chk) является «наиболее специфичным», который можно вызвать без ошибки типа.

См. JLS 15.12.2.5 о том, как формально определено «наиболее конкретное».

...