public class Info {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
单类型参数的泛型类
public class Info {
private T value;
public Info() { }
public Info(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
@Override
public String toString() {
return "Info{" "value=" value '}';
}
}
public class GenericsClassDemo01 {
public static void main(String[] args) {
Info info = new Info<>();
info.setValue(10);
System.out.println(info.getValue());
Info info2 = new Info<>();
info2.setValue("xyz");
System.out.println(info2.getValue());
}
}
// Output:
// 10
// xyz
public static void main(String[] args) {
Info info = new Info();
info.setValue(10);
System.out.println(info.getValue());
info.setValue("abc");
System.out.println(info.getValue());
}
示例说明:
上面的例子,不会产生编译错误,也能正常运行。但这样的调用就失去泛型类型的优势。
多个类型参数的泛型类
public class MyMap {
private K key;
private V value;
public MyMap(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "MyMap{" "key=" key ", value=" value '}';
}
}
public class GenericsClassDemo02 {
public static void main(String[] args) {
MyMap map = new MyMap<>(1, "one");
System.out.println(map);
}
}
// Output:
// MyMap{key=1, value=one}
泛型类的类型嵌套
public class GenericsClassDemo03 {
public static void main(String[] args) {
Info info = new Info("Hello");
MyMap> map = new MyMap<>(1, info);
System.out.println(map);
}
}
// Output:
// MyMap{key=1, value=Info{value=Hello}}
2.2. 泛型接口
接口也可以声明泛型。
泛型接口语法形式:
public interface Content {
T text();
}
泛型接口有两种实现方式:
实现接口的子类明确声明泛型类型
public class GenericsInterfaceDemo01 implements Content {
private int text;
public GenericsInterfaceDemo01(int text) {
this.text = text;
}
@Override
public Integer text() { return text; }
public static void main(String[] args) {
GenericsInterfaceDemo01 demo = new GenericsInterfaceDemo01(10);
System.out.print(demo.text());
}
}
// Output:
// 10
实现接口的子类不明确声明泛型类型
public class GenericsInterfaceDemo02 implements Content {
private T text;
public GenericsInterfaceDemo02(T text) {
this.text = text;
}
@Override
public T text() { return text; }
public static void main(String[] args) {
GenericsInterfaceDemo02 gen = new GenericsInterfaceDemo02<>("ABC");
System.out.print(gen.text());
}
}
// Output:
// ABC
public class GenericsMethodDemo01 {
public static void printClass(T obj) {
System.out.println(obj.getClass().toString());
}
public static void main(String[] args) {
printClass("abc");
printClass(10);
}
}
// Output:
// class java.lang.String
// class java.lang.Integer
泛型方法中也可以使用可变参数列表
public class GenericVarargsMethodDemo {
public static List makeList(T... args) {
List result = new ArrayList();
Collections.addAll(result, args);
return result;
}
public static void main(String[] args) {
List ls = makeList("A");
System.out.println(ls);
ls = makeList("A", "B", "C");
System.out.println(ls);
}
}
// Output:
// [A]
// [A, B, C]
4. 类型擦除
Java 语言引入泛型是为了在编译时提供更严格的类型检查,并支持泛型编程。不同于 C 的模板机制,Java 泛型是使用类型擦除来实现的,使用泛型时,任何具体的类型信息都被擦除了。
泛型不能用于显式地引用运行时类型的操作之中,例如:转型、instanceof 操作和 new 表达式。因为所有关于参数的类型信息都丢失了。当你在编写泛型代码时,必须时刻提醒自己,你只是看起来好像拥有有关参数的类型信息而已。
正是由于泛型时基于类型擦除实现的,所以,泛型类型无法向上转型。
向上转型是指用子类实例去初始化父类,这是面向对象中多态的重要表现。
Integer 继承了 Object;ArrayList 继承了 List;但是 List 却并非继承了 List。
这是因为,泛型类并没有自己独有的 Class 类对象。比如:并不存在 List.class 或是 List .class ,Java 编译器会将二者都视为 List.class。
List list = new ArrayList<>();
List list2 = list; // Erorr
6. 类型边界
有时您可能希望限制可在参数化类型中用作类型参数的类型。类型边界可以对泛型的类型参数设置限制条件。例如,对数字进行操作的方法可能只想接受 Number 或其子类的实例。
要声明有界类型参数,请列出类型参数的名称,然后是 extends 关键字,后跟其限制类或接口。
类型边界的语法形式如下:
示例:
public class GenericsExtendsDemo01 {
static > T max(T x, T y, T z) {
T max = x; // 假设x是初始最大值
if (y.compareTo(max) > 0) {
max = y; //y 更大
}
if (z.compareTo(max) > 0) {
max = z; // 现在 z 更大
}
return max; // 返回最大对象
}
public static void main(String[] args) {
System.out.println(max(3, 4, 5));
System.out.println(max(6.6, 8.8, 7.7));
System.out.println(max("pear", "apple", "orange"));
}
}
// Output:
// 5
// 8.8
// pear
示例说明:
上面的示例声明了一个泛型方法,类型参数 T extends Comparable 表明传入方法中的类型必须实现了 Comparable 接口。
类型边界可以设置多个,语法形式如下:
注意:extends 关键字后面的第一个类型参数可以是类或接口,其他类型参数只能是接口。
示例:
public class GenericsExtendsDemo02 {
static class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }
static class D1 { /* ... */ }
static class D2 { /* ... */ } // 编译报错
static class E extends A implements B, C { /* ... */ }
public static void main(String[] args) {
D1 demo1 = new D1<>();
System.out.println(demo1.getClass().toString());
D1 demo2 = new D1<>(); // 编译报错
}
}
7. 类型通配符
类型通配符一般是使用 ? 代替具体的类型参数。例如 List 在逻辑上是 List ,List 等所有 List <具体类型实参> 的父类。
7.1. 上界通配符
可以使用上界通配符来缩小类型参数的类型范围。
它的语法形式为:
public class GenericsUpperBoundedWildcardDemo {
public static double sumOfList(List extends Number> list) {
double s = 0.0;
for (Number n : list) {
s = n.doubleValue();
}
return s;
}
public static void main(String[] args) {
List li = Arrays.asList(1, 2, 3);
System.out.println("sum = " sumOfList(li));
}
}
// Output:
// sum = 6.0
7.2. 下界通配符
下界通配符将未知类型限制为该类型的特定类型或超类类型。
注意:上界通配符和下界通配符不能同时使用。
它的语法形式为:
public class GenericsLowerBoundedWildcardDemo {
public static void addNumbers(List super Integer> list) {
for (int i = 1; i <= 5; i ) {
list.add(i);
}
}
public static void main(String[] args) {
List list = new ArrayList<>();
addNumbers(list);
System.out.println(Arrays.deepToString(list.toArray()));
}
}
// Output:
// [1, 2, 3, 4, 5]
7.3. 无界通配符
无界通配符有两种应用场景:
可以使用 Object 类中提供的功能来实现的方法。
使用不依赖于类型参数的泛型类中的方法。
语法形式:
public class GenericsUnboundedWildcardDemo {
public static void printList(List> list) {
for (Object elem : list) {
System.out.print(elem " ");
}
System.out.println();
}
public static void main(String[] args) {
List li = Arrays.asList(1, 2, 3);
List ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);
}
}
// Output:
// 1 2 3
// one two three
7.4. 通配符和向上转型
前面,我们提到:泛型不能向上转型。但是,我们可以通过使用通配符来向上转型。
public class GenericsWildcardDemo {
public static void main(String[] args) {
List intList = new ArrayList<>();
List numList = intList; // Error
List extends Integer> intList2 = new ArrayList<>();
List extends Number> numList2 = intList2; // OK
}
}
扩展阅读:Oracle 泛型文档
8. 泛型的约束
泛型类型的类型参数不能是值类型
Pair p = new Pair<>(8, 'a'); // 编译错误
不能创建类型参数的实例
public static void append(List list) {
E elem = new E(); // 编译错误
list.add(elem);
}
不能声明类型为类型参数的静态成员
public class MobileDevice {
private static T os; // error
// ...
}
类型参数不能使用类型转换或 `instanceof`
public static void rtti(List list) {
if (list instanceof ArrayList) { // 编译错误
// ...
}
}
List li = new ArrayList<>();
List ln = (List) li; // 编译错误