Как работает различие неоднозначности полиморфа? - PullRequest
4 голосов
/ 17 апреля 2010

Учитывая, что у меня есть класс с двумя конструкторами:

public class TestClass {
    ObjectOne o1;
    ObjectTwo o2;

    public TestClass(ObjectOne o1) {
        // ..
    }

    public TestClass(ObjectTwo o2) {
        // ..
    }
}

Пожалуйста, предположите, что ObjectOne - это тип interface, а ObjectTwo implements ObjectOne. Что произойдет, если я позвоню:

new TestClass(null);

Как определить правильный метод для вызова? И кто это определяет? Есть ли различия между Java и другими языками ООП?

Ответы [ 6 ]

16 голосов
/ 18 апреля 2010

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

Разрешение метода - сложная вещь, и в этом случае то, будет ли код компилироваться вообще, зависит от того, какие типы используются. Существует множество ситуаций, в которых код будет компилироваться. См. Код ниже (обратите внимание, что String implements CharSequence):

public class MyClass {
    MyClass(CharSequence charSeq) {
        System.out.println("CharSequence");
    }
    MyClass(String s) {
        System.out.println("String");
    }   
    public static void main(String[] args) {
        new MyClass(null); // prints "String"
        new MyClass(""); // prints "String"
        new MyClass((CharSequence) null); // prints "CharSequence"
        new MyClass((CharSequence) "");   // prints "CharSequence"
    }
}

Обратите внимание, что без приведения выбрана перегрузка String. Это в точности как указано в JLS:

JLS 15.12.2.5 Выбор наиболее специфического метода

Если более одного метода-члена доступны и применимы к вызову метода, необходимо выбрать один, чтобы предоставить дескриптор для отправки метода во время выполнения. Язык программирования Java использует правило, согласно которому выбирается самый специфический метод.

A String is-a CharSequence, но не все CharSequence is-a String. Следовательно, String является более точным , чем CharSequence, поэтому в приведенном выше примере выбрана перегрузка String.


Стоит отметить, что этот точный вопрос (по сути) появился в замечательных Java Puzzlers (настоятельно рекомендуется), в частности Puzzle 46 : Случай запутанного конструктора

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

Ключом к пониманию этой загадки является то, что тест, для которого метод или конструктор является наиболее специфичным, не использует фактические параметры : параметры, появляющиеся в вызове. Они используются только для определения того, какие перегрузки применимы. Как только компилятор определяет, какие перегрузки применимы и доступны, он выбирает наиболее специфическую перегрузку, используя только формальные параметры : параметры, указанные в объявлении.


Я закрою цитатой из Effective Java 2nd Edition , Item 41: Используйте разумную перегрузку :

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

Само собой разумеется, эта книга также настоятельно рекомендуется .

Смотри также

5 голосов
/ 17 апреля 2010

Нет магии. Вы должны привести нулевой параметр к ObjectOne или ObjectTwo.

Итак:

 new TestClass((ObjectOne) null);

или

 new TestClass((ObjectTwo) null);
1 голос
/ 17 апреля 2010

Ошибка компиляции: конструктор TestClass неоднозначен

1 голос
/ 17 апреля 2010

Очевидно, он не скомпилируется.

1 голос
/ 17 апреля 2010

Компилятор потерпит неудачу, потому что вы делаете неоднозначный вызов конструктора. Компилятор C # имеет такое же поведение.

Чтобы это работало на любом языке, вы должны привести null к одному из двух типов, чтобы устранить неоднозначность при вызове конструктора.

0 голосов
/ 18 апреля 2010

Я не знаю, будет ли работать актерский состав. Вы можете попробовать это:

ObjectOne o1 = null;
new TestClass(o1);
...