Effective Java 的笔记,代码、英语原文为主,批注、翻译为辅。

Item 26: Don’t use raw types

请不要使用原始类型( raw types 实在不知道该怎么翻译🤷🏻‍♀️,中文版书籍译为原生态类型)

Should not use raw types

As mentioned throughout this book, it pays to discover errors as soon as possible after they are made, ideally at compile time.

Java 作为静态、强类型语言,最好在编译期间发现错误,写代码也倾向于:便于在编译期发现错误的代码,使用编译时信息协助排除大量运行时问题,越早发现错误越好。

As noted earlier, it is legal to use raw types (generic types without their type parameters), but you should never do it. If you use raw types, you lose all the safety and expressiveness benefits of generics.

如果使用 raw types ,就失去了泛型在安全性和描述性方面的所有优势。

While you shouldn’t use raw types such as List, it is fine to use types that are parameterized to allow insertion of arbitrary objects, such as List<Object>.

List VS List<Object>

Just what is the difference between the raw type List and the parameterized type List<Object>?

Loosely speaking, the former has opted out of the generic type system, while the latter has explicitly told the compiler that it is capable of holding objects of any type. While you can pass a List<String> to a parameter of type List, you can’t pass it to a parameter of type List<Object>.

raw type List: 逃避了泛型检查。

parameterized type List<Object>: 明确告知编译器能够持有任意类型的对象。

List<String> 可以传递给 List 的参数,但不能传给 List<Object> 的参数。

There are sub-typing rules for generics, and List is a subtype of the raw type List, but not of the parameterized type List<Object> .

As a consequence, you lose type safety if you use a raw type such as List, but not if you use a parameterized type such as List<Object>.

For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Fails at runtime - unsafeAdd method uses a raw type (List)! 
public class Raw {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, Integer.valueOf(42));
        String s = strings.get(0); // Has compiler-generated cast
    }

    private static void unsafeAdd(List list, Object o) {
        list.add(o);
    }
}

IntelliJ IDEA报错:

1
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')

Covariant VS Invariant

Arrays are covariant. Generics are invariant.

数组是协变(covariant)的,泛型、集合是不变(invariant)的。

covariant:

This scary-sounding word means simply that if Sub is a subtype of Super, then the array type Sub[] is a subtype of the array type Super[].

invariant:

for any two distinct types Type1 and Type2, List<Type1> is neither a subtype nor a supertype of List<Type2>.

e.g. P=C —> P[ ]=C[ ]

不变的举例:

invariant

泛型的优点:

  • 更好的安全性:编译期就发现错误,保证类型安全
  • 更好的可读性:省去强制类型转换

Must use raw types

There are a few minor exceptions to the rule that you should not use raw types. You must use raw types in class literals. The specification does not permit the use of parameterized types (though it does permit array types and primitive types). In other words, List.class, String[].class, and int.class are all legal, but List<String>.class and List<?>.class are not.

Summary

In summary, using raw types can lead to exceptions at runtime, so don’t use them. They are provided only for compatibility and interoperability with legacy code that predates the introduction of generics.

As a quick review, Set<Object> is a parameterized type representing a set that can contain objects of any type, Set<?> is a wildcard type representing a set that can contain only objects of some unknown type, and Set is a raw type, which opts out of the generic type system. The first two are safe, and the last is not.

For quick reference, the terms are summarized in the following table:

Term Example
Parameterized type List<String>
Actual type Parameter String
Generic type List<E>
Formal type parameter E
Unbounded wildcard type List<?>
Raw type List
Bounded type parameter <E extends Number>
Recursive type bound <T extends Comparable<T>>
Bounded wildcard type List<? extends Number>
Generic method static <E> List<E> asList(E[] a)
Type token String.class