Dawn's Blogs

分享技术 记录成长

0%

Java高级 (7) 泛型

泛型

为什么需要泛型

那么为什么要有泛型呢,直接 Object 不是也可以存储数据吗?

  • 解决元素存储的安全性问题,确定数据类型。
  • 解决获取数据元素时,需要类型强制转换的问题。

Java 泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException 异常。同时,代码更加简洁、健壮。

image-20230201181245221

image-20230201181302622

自定义泛型结构

自定义泛型类 泛型接口

泛型接口与泛型类类似,这里以泛型类为例子。

父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型

  • 子类不保留父类的泛型:
    • 擦除,相当于泛型类型为 Object。
    • 指定具体类型,如指定为 Integer。
  • 子类保留父类的泛型:
    • 全部保留。
    • 部分保留,不保留的部分需要指定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Father<T1, T2> {
}

// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{
}
// 2)具体类型
class Son2<A, B> extends Father<Integer, String> {
}

// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
}

自定义泛型方法

方法也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时参数的类型就是传入数据的类型。泛型方法的格式:

1
2
3
4
5
6
7
8
[方法权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) [抛出的异常];

// 泛型方法举例
public static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o);
}
}

通配符

使用类型通配符:?,比如:List<?>List<String>List<Object> 等各种泛型 List 的父类

注意:

  • 将任意元素加入到其中不是类型安全的
    • 唯一的例外的是 null,它是所有类型的成员。
1
2
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 编译时错误
  • 可以调用 get() 方法并使用其返回值:返回值是一个未知类型,但总是一个 Object 类型。

有限制的通配符

  • 指定通配符上限 <? extends ClassName>:只允许泛型为 className 以及 ClassName 的子类。
  • 指定通配符下限 <? super ClassName>:只允许泛型为 ClassName 以及 ClassName 的父类。
  • 指定接口 <? extends InterfaceName>:只允许泛型为实现 InterfaceName 接口的实现类。