2013-02-26 6 views
1

Следующий код из эффективных Java книги:Почему этот общий вызов метода не работает?

Set<Integer> integers = ... ; 
Set<Double> doubles = ... ; 
Set<Number> numbers = union(integers, doubles); 

Этот код не компилируется и автор предлагает, чтобы обойти эту проблему, указав компилятору точный тип, как следующее:

Set<Number> numbers = Union.<Number>union(integers, doubles) 

Если подпись профсоюза выглядит следующим образом, почему бы предыдущая программа не была скомпилирована? Также как называется эта обходная идиома?

public static <E> Set<E> union(Set<? extends E> s1, 
Set<? extends E> s2) 

ответ

5

Обратите внимание, что Double и Integer не только расширяют число, но также реализуют Comparable. Таким образом, возвращаемый тип, догаданный компилятором, был бы установленным < Номер & Сопоставимый>, который не может быть отлит до Set < Номер>. Вам нужно сообщить компилятору, какой из следующих типов использовать. С последующим кодом вам не нужен точный тип.

interface X {} 
class U implements X {} 
class V implements X {} 

public static void main(String[] args) { 
    Set<U> integers = new HashSet<U>(); 
    Set<V> doubles = new HashSet<V>(); 
    Set<X> numbers = union(integers, doubles); 
} 
public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2) { 
    return null; 

} 

Но если вы его немного измените, вы получите исходную ошибку.

interface X {} 
interface Y {} 
class U implements X, Y {} 
class V implements X, Y {} 

public static void main(String[] args) { 
    Set<U> integers = new HashSet<U>(); 
    Set<V> doubles = new HashSet<V>(); 
    Set<X> numbers = union(integers, doubles); 
} 
public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2) { 
    return null; 

} 
+0

Почему вы возвращаете нуль? Разве это не должно возвращать объединение двух проходов в наборах? – Geek

+0

поведение во время выполнения не имеет значения при демонстрации проблем компиляции. – ijrandom

+0

Хорошее объяснение. – nkr

1

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

1

Если метод объединения определяется как <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2), старые компиляторы Java недостаточно интеллектуальны, чтобы правильно вывести тип возврата. Таким образом, компилятору необходимо сообщить, какой тип вы хотите вернуть из метода, чтобы обеспечить безопасность типа. (Я думаю, что компиляторы Java 7 могут сделать это правильно, но я не уверен).

Я также не знаю названия для этой «идиомы», это просто называется общей функцией.

+0

Он также терпит неудачу с Java 7. –

1

Компилятор Java пытается сузить возвращаемый тип настолько, насколько это возможно. После попытки макета этого примера, я получаю следующее сообщение об ошибке компилятора без указания .<Number>union:

EffectiveJava.java:19: incompatible types 
found : java.util.Set<java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>> 
required: java.util.Set<java.lang.Number> 
     Set<Number> numbers = union(integers, doubles); 

Он пытается включить Comparable в «Е», потому что оба Целые и парных также Comparable. Вот почему вы должны сказать компилятору, нет, я просто хочу Number с .<Number>union.

Насколько я знаю, я не знаю, есть ли имя для идиомы.

0

Когда вы говорите

Set numbers = Union.union(integers, doubles)

Здесь Союз имя класса, содержащего метод статический метод объединения

static Set union(Set s1, Set s2)

так что если вы класс имя GenericDemo, где определен метод союза , код, который вы напишете -

Set numbers = GenericDemo.union(integers, doubles)

вы можете заменить GenericDemo экземпляром объекта, если объединение является не статическим методом.

0

Все о принципах и концепциях дженериков. Я берусь от примера, отправленного ijrandom. Возьмем первый сценарий, когда U и V реализуют только X. Пока мы не назначаем вывод метода в ссылочную переменную. Мы назвали метод объединения, как указано ниже:

объединение (целые числа, удваивается);

Мы знаем, что для дженериков фактический тип параметра определяется типом, который вы указываете при вызове метода, поэтому в этом случае возвращаемым типом метода будет множество X, просто переместите курсор над вызовом метода, вы увидите, как java-компилятор определил фактический тип параметра во время компиляции.

В нашем втором сценарии мы еще раз вызвать метод объединения:

объединение (целые числа, в два раза); // Обратите внимание, мы еще не приписали возвращаемое значение переменной.

Переместить курсор над вызовом метода, вы заметите, что тип возвращаемого значения для метода изменяется на Набор неизвестных расширений X, поэтому здесь компилятор не может полностью разрешить фактический тип, так как U и V реализуют как X, так и X и X не является Y. Следовательно, как только вы присваиваете значение переменной, возникает ошибка, поскольку компилятор все еще не в состоянии идентифицировать фактический тип. В таком сценарии вам нужно сообщить компилятору, какой фактический тип использовать.