Java 泛型
泛型方法、泛型类、类型通配符与类型擦除。
泛型方法
Declare <T> before the return type and the compiler infers it at the call site. One method then works safely for many types with no casts and no ClassCastException.
public class GenericMethod {
static <T> void print(T value) {
System.out.println(value);
}
static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
public static void main(String[] args) {
print("text");
print(42);
System.out.println(max(3, 7));
System.out.println(max("apple", "banana"));
}
}text
42
7
banana泛型类
class Box<T> makes the whole class generic. T is convention for Type, with E for Element and K/V for Key/Value. Generics require wrapper types, not primitives.
class Box<T> {
private T value;
void set(T value) {
this.value = value;
}
T get() {
return value;
}
}
public class GenericClass {
public static void main(String[] args) {
Box<String> b = new Box<>();
b.set("hello");
System.out.println(b.get());
Box<Integer> n = new Box<>();
n.set(100);
System.out.println(n.get() + 1);
}
}hello
101类型通配符
? extends T means T or a subtype — such a collection can be read but not written safely. ? super T means T or a supertype — writable, but reads only give Object.
import java.util.ArrayList;
import java.util.List;
public class Wildcard {
static double sum(List<? extends Number> nums) {
double total = 0;
for (Number n : nums) {
total += n.doubleValue();
}
return total;
}
static void addInts(List<? super Integer> list) {
list.add(1);
list.add(2);
}
public static void main(String[] args) {
System.out.println(sum(List.of(1, 2, 3)));
System.out.println(sum(List.of(1.5, 2.5)));
List<Number> target = new ArrayList<>();
addInts(target);
System.out.println(target);
}
}6.0
4.0
[1, 2]类型擦除
Generics exist only at compile time: after erasure List<String> and List<Integer> are both List. Hence no new T(), no T.class and no direct generic arrays.
import java.util.ArrayList;
import java.util.List;
public class Erasure {
public static void main(String[] args) {
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
System.out.println(a.getClass() == b.getClass());
System.out.println(a.getClass().getSimpleName());
List raw = a;
raw.add(123);
System.out.println(raw.size());
}
}true
ArrayList
1String throws ClassCastException later — exactly what generics eliminate.泛型方法
在返回类型前写 <T> 声明类型参数,调用时编译器自动推断。这样同一个方法可以安全地处理不同类型,不用强转也不会 ClassCastException。
public class GenericMethod {
static <T> void print(T value) {
System.out.println(value);
}
static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
public static void main(String[] args) {
print("text");
print(42);
System.out.println(max(3, 7));
System.out.println(max("apple", "banana"));
}
}text
42
7
banana泛型类
类名后写 <T> 让整个类使用同一类型参数。T 只是约定名(Type),也可以用 E(Element)、K/V(Key/Value)。泛型不能是基本类型,要用包装类。
class Box<T> {
private T value;
void set(T value) {
this.value = value;
}
T get() {
return value;
}
}
public class GenericClass {
public static void main(String[] args) {
Box<String> b = new Box<>();
b.set("hello");
System.out.println(b.get());
Box<Integer> n = new Box<>();
n.set(100);
System.out.println(n.get() + 1);
}
}hello
101类型通配符
? extends T 表示「T 或 T 的子类」,这样的集合只能读不能写(写入不安全);? super T 表示「T 或 T 的父类」,可以写但读出来只是 Object。
import java.util.ArrayList;
import java.util.List;
public class Wildcard {
static double sum(List<? extends Number> nums) {
double total = 0;
for (Number n : nums) {
total += n.doubleValue();
}
return total;
}
static void addInts(List<? super Integer> list) {
list.add(1);
list.add(2);
}
public static void main(String[] args) {
System.out.println(sum(List.of(1, 2, 3)));
System.out.println(sum(List.of(1.5, 2.5)));
List<Number> target = new ArrayList<>();
addInts(target);
System.out.println(target);
}
}6.0
4.0
[1, 2]类型擦除
泛型只在编译期存在,编译后 List<String> 和 List<Integer> 都是 List,这叫类型擦除。所以不能 new T()、不能 T.class、也不能对泛型数组直接创建。
import java.util.ArrayList;
import java.util.List;
public class Erasure {
public static void main(String[] args) {
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
System.out.println(a.getClass() == b.getClass());
System.out.println(a.getClass().getSimpleName());
List raw = a;
raw.add(123);
System.out.println(raw.size());
}
}true
ArrayList
1String 用就会 ClassCastException。这正是泛型要消灭的问题。