Почему этот общий вызов метода не работает?
Следующий код взят из книги Effective 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)
6 ответов
Обратите внимание, что Double и Integer не только расширяют Number, но также реализуют 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;
}
Компилятор 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
в "E", потому что и целые и двойные числа также Comparable
, Вот почему вы должны сказать компилятору, нет, я просто хочу Number
с .<Number>union
,
Насколько я знаю, я не знаю, есть ли название для этой идиомы.
Если метод объединения определен как <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2)
более старые Java-компиляторы недостаточно умны, чтобы правильно определить тип возвращаемого значения. Таким образом, компилятору необходимо сообщить, какой тип вы хотите вернуть из метода, чтобы обеспечить безопасность типов. (Я думаю, что компиляторы Java 7 могли бы вывести это правильно, но я не уверен).
Я также не знаю названия для этой "идиомы", она просто называется универсальной функцией.
Единственная проблема заключается в том, что компилятор не достаточно умен, чтобы выяснить, какой тип заменить E
, так что вы должны указать это явно. Для этой идиомы нет названия, просто вы явно указываете параметры универсального типа.
Когда ты сказал
Set numbers = Union.union(integers, doubles)
Здесь Union это имя класса, содержащего метод static метод union
static Set union(Set s1, Set s2)
так что если вы называете класс GenericDemo, где определен метод union, код, который вы напишите
Set numbers = GenericDemo.union(integers, doubles)
вы можете заменить GenericDemo на экземпляр объекта, если объединение не является статическим методом.
Все о принципах и понятиях обобщенных обозначений. Я перехожу к примеру, опубликованному ijrandom. Давайте рассмотрим первый сценарий, в котором U и V реализуют только X. В настоящее время мы не назначаем выходные данные метода для ссылочной переменной. Мы вызвали метод объединения, как указано ниже:
объединение (целые, двойные);
Мы знаем, что для обобщенных типов фактический тип параметра определяется типом, который вы указываете при вызове метода, поэтому в этом случае тип возвращаемого значения для метода будет Set of X, просто наведите курсор на вызов метода, и вы увидите, как определил компилятор java фактический тип параметра во время компиляции.
В нашем втором сценарии мы снова вызываем метод union:
union (integer, doubles);// Обратите внимание, что мы еще не присвоили возвращаемое значение переменной.
Наведите курсор на вызов метода, и вы заметите, что возвращаемый тип для изменений метода в Set of unknown расширяет X, поэтому здесь компилятор не может полностью разрешить фактический тип, поскольку U и V реализуют и X, и Y, а X не Y. Следовательно, как только вы присваиваете значение переменной, выдается ошибка, так как компилятор все еще не может идентифицировать фактический тип. В таком случае вам нужно указать компилятору, какой тип использовать.