Допускаются ли необработанные типы в границах дженериков? В чем их точное значение?
class Foo<T>{}
class Bar<T extends Foo>{}
вместо того, чтобы писать
class Bar<U, T extends Foo<U>> {}
вторая версия менее удобна в использовании; можно первые считать как-то равноценными, хоть и разными?
class Bar<T extends Foo<?>> {}
это типобезопасная версия первого случая?




Использование необработанных типов вместо типов с подстановочными знаками в качестве границ параметра типа не изменяет аргументы типа, разрешенные для класса (Bar<Foo>, Bar<Foo<?>>, Bar<Foo<String>> и т. д.), Но влияет на использование параметра типа в классе. Например, используя List в качестве привязки:
class Bar1<T extends List<?>> {
void m(T t) {
t.add("abc"); // type error
t.add(123); // type error
}
}
class Bar2<T extends List> { // warning about raw List
void m(T t) {
t.add("abc"); // warning about raw List
t.add(123); // warning about raw List
}
}
Поскольку аргумент типа E для List<E> неизвестен в обоих случаях, добавлять что-либо в список небезопасно, и первый класс правильно вызывает ошибки компилятора. Однако использование привязки к необработанному типу отключает эту проверку типа и рассматривает метод add(E) как add(Object), разрешая все, что угодно. Проблема становится очевидной, когда List<Integer> передается второму классу:
Bar2<List<Integer>> bar2 = new Bar2<>();
List<Integer> list = new ArrayList<>();
bar2.m(list);
System.out.println(list); // prints [abc, 123]
Integer i = list.get(0); // throws ClassCastException
List<Integer> заканчивается элементом String, что приводит к исключению ClassCastException во время выполнения, когда он пытается установить для String значение Integer. Вот почему сырые типы опасны.
Are raw types allowed in generics bounds? What is their exact meaning?
Да, они разрешены в пределах дженериков. Они означают то же самое, что и необработанные типы в других местах.
Процитирую пользователя John Feminella:
List: список без параметра типа. Это список, элементы которого
любого типа - элементы могут быть разных типов.List<?>: список с параметром неограниченного типа. Его элементы имеют
конкретный, но неизвестный тип; все элементы должны быть одного типа.List<T extends E>: список с параметром типа T.
предоставленный тип для T должен иметь тип, расширяющий E, или это недопустимый тип для параметра.Общая цель универсальных типов - вызывать ошибки компиляции, а не ClassCastExceptions во время выполнения.
Вот несколько примеров с комментариями. Дайте мне знать, если у вас возникнут вопросы.
class Foo<U> extends ArrayList<U> {}
class BarRaw<T extends Foo> {
void doSomething(T t) {
// all you know is that `t` extends ArrayList holding any kinds of Objects
// raw types are dangerous because..
// the types are only checked at runtime, rather than compile time.
// warnings about raw types, but no compile error
t.set(0, Integer.valueOf(1));
t.set(1, "someString");
Object obj0 = t.get(0); // obj0 is truly an `Integer`
Object obj1 = t.get(1); // obj1 is truly a `String`
// valid, casting a true `Integer` to a `Integer`
Integer int0 = (Integer) t.get(0);
// valid, but ClassCastException at runtime! Casting a true `String` to a `Integer`
Integer int1 = (Integer) t.get(1);
}
}
class BarParam1<U, T extends Foo<U>> {
void doSomething(T t, U u) {
// `t` extends ... `ArrayList<U>`, can only add elements of type `U`
// and the elements you get are guaranteed to be of type `U`
t.set(0, u); // valid
t.set(1, new Object()); // compile err, can only set elements of type `U`
t.set(1, (U) new Object()); // valid, but ClassCastException at runtime
t.set(2, Integer.valueOf(0)); // compile err, can only set elements of type `U`
U u0 = t.get(0); // valid
Object obj0 = t.get(0); // valid, any Object can be an Object..
Integer int0 = t.get(0); // compile err, can't convert from `U` to `Integer`
Integer int1 = (Integer) t.get(0); // valid but DANGER
if (obj0 instanceof Integer) {
Integer int2 = (Integer) obj0; // valid and safe since you checked
}
}
}
class BarParam2<U extends Number, T extends Foo<U>> {
void doSomething(T t, U u) {
// `T` extends ... `ArrayList<U extends Number>`
// can only add elements of type `U extends Number`
// and the elements you get are guaranteed to be of type `U extends Number`
t.set(0, u); // valid
t.set(1, new Object()); // compile err, can only set elements of type `U` exactly
t.set(1, (U) new Object()); // valid, but ClassCastException at runtime
t.set(2, Integer.valueOf(0)); // compile err, can only set elements of type `U` exactly
U u0 = t.get(0); // valid
Object obj0 = t.get(0); // valid, any Object can be an Object..
Integer int0 = t.get(0); // compile err, can't convert from `U` to `Integer`
Number num0 = t.get(0); // valid, `U` is guaranteed to extend `Number`
Integer int1 = (Integer) t.get(0); // valid but DANGER
if (obj0 instanceof Integer) {
Integer int2 = (Integer) obj0; // valid and safe since you checked
}
}
}
class BarWild1<U, T extends Foo<?>> {
void doSomething(T t, Number u) {
// all compile err, no idea what `?` is
t.set(0, u);
t.set(1, new Object());
t.set(1, (String) new Object());
String u0 = t.get(0); // compile err, no idea what `?` is other than some Object
Object obj0 = t.get(0); // valid, `?` extends `Object` since all objects do.
}
}
class BarWild2<T extends Foo<? extends Number>> {
void doSomething(T t, Number u) {
// `t` extends ... `ArrayList<? extends Number>`
// can only add elements of type `? extends Number`
// and the elements you get are guaranteed to be of type `? extends Number`
// all compile err, no idea what exact type `?` is
t.set(0, u);
t.set(1, new Object());
t.set(1, (Number) new Object());
t.set(2, Integer.valueOf(0));
Number num0 = t.get(0); // valid, we know that the elements extend `Number`
Object obj0 = t.get(0); // valid, any Object can be an Object..
Integer int0 = t.get(0); // compile err, all we know is that the elements extend `Number`
Integer int1 = (Integer) t.get(0); // valid but DANGER
if (obj0 instanceof Integer) {
Integer int2 = (Integer) obj0; // valid and safe since you checked
}
}
}
Можете ли вы дать нам конкретный пример использования? Возможно, мы увидим, существует ли альтернатива.