为什么在泛型中,不要使用原始类型(不带类型参数的泛型)?比如要使用List<?>,不要简单的使用List
- 使用不带类型参数的泛型,会丢掉泛型所有的安全性和富有表现力的好处
- 之所有不带类型参数的泛型合法,是为了兼容java5之前的旧代码
- 在class literals中必须要用raw types,比如:List.class, String[].class,int.class,而不能使用List<String>.class 和 List<?>.class。
- 在instanceof操作中,要用raw types,比如:
if (o instanceof Set) { // Raw type
Set<?> s = (Set<?>) o; // Wildcard type
...
}
泛型中,Set<String>,Set<Object>,Set<?>,以及Set的区别?
- Set<String>代表set中的元素是String类型;Set<String>,Set<Object>和Set<?> 都是Set的子类型;Set<Object>代表set中的元素是Object类型;Set<?>是一个通配符类型,代表set中的元素只能是某种未知类型的Objects;而Set是raw type,它不属于泛型系统。
在泛型中,相对于Array,为什么优先使用List?
- Array是协变的(covariant),即共同变化,比如
Sub 是 Super的子类型,Sub[] 也是 Super[]的子类型
而List是不变的(invariant),即对于两个不同的类型Type1 和 Type2,则List<Type1>既不是List<Type2>的子类型,也不是List<Type2>的超类型。
- Array是reified(物化, 具体的),即它确保运行期类型的正确性;而List是erasure(擦除化的),即它确保编译期类型的正确性,而在运行期时,它的元素的具体类型被丢弃(erasure是为了兼容Java5之前的代码)
- 由于上述两点,java 禁止创建泛型数组
拥抱泛型
- Object-based 应该重构成generic types
如何使用有边界通配符以增加API的灵活性?
- PECS stands for producer-extends, consumer-super
- E 替换成? extends E 或者? super E
- 不要使用有边界通配符作为return types
- 在一个方法签名中,如果一个类型参数E只出现一次,则把替换成通配符?
为什么尽量不要把泛型(generics)和不定参数(varargs)混合使用
- 因为varargs是建立在数组上的抽象,而数组和泛型有着不和谐的交互
解释泛型中的type token(类型标记)
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(Objects.requireNonNull(type), type.cast(instance));
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
public static void main(String[] args) {
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
f.putFavorite(Integer.class, 0xcafebabe);
f.putFavorite(Class.class, Favorites.class);
String favoriteString = f.getFavorite(String.class);
int favoriteInteger = f.getFavorite(Integer.class);
Class<?> favoriteClass = f.getFavorite(Class.class);
System.out.printf("%s %x %s%n", favoriteString, favoriteInteger, favoriteClass.getName());
}
}