文章目录

  • 1. 泛型
  • 1.1 泛型的实现
  • 1.2 泛型的好处
  • 1.3 泛型的用途
  • 1.4 小结
  • 2. 常用通配符
  • 2.1 理解通配符
  • 2.2 总结


1. 泛型

Java 泛型(generics)是 JDK 5 中引入的一个新特性, **泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。**泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

1.1 泛型的实现

Java 的泛型是伪泛型,这是因为 Java 在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。

Java在编译后的字节码(.class)文件中是不包含泛型中的类型信息的,使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉,这个过程就称为类型擦除。

如在代码中定义的List<Object>List<String>等类型,在编译之后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。 因此,对于JVM来说,List<Object>List<String>就是同一个类,所以,泛型实际上是Java语言的一个语法糖,又被叫做伪泛型。

而C++的泛型则来源于C++引入的STL模板,而STL模板是一套编译时多态技术,会在编译时针对不同的模板参数静态实例化,生成大量代码,运行速度会快很多,是图灵完备的(图灵完全性通常指具有无限存储能力的通用物理机器或编程语言)。

1.2 泛型的好处

  • 提交了java的类型安全
    泛型在很大程度上来提高了java的程序安全。例如在没有泛型的情况下,很容易将字符串 123 转成 Integer 类型的 123 亦或者 Integer 转成 String,而这样的错误是在编译期无法检测。而使用泛型,则能很好的避免这样的情况发生。
  • 不需要烦人的强制类型转换
    泛型之所以能够消除强制类型转换,那是因为程序员在开发的时候就已经明确了自己使用的具体类型,这不但提高了代码的可读性,同样增加了代码的健壮性。
  • 提高了代码的重用性:泛型的程序设计,意味着编写的代码可以被很多不同类型的对象所重用
// 不适用泛型
Pair p = new Pair("string",222);
String key = (String) p.getKey();		// 正常
String value = (String) p.getValue();	// 编译时报错


// 使用泛型
Pair<String,Integer> p = new Pair("string",222);
String key = (String) p.getKey();		// 正常
String value = (String) p.getValue();	// 在编译之前,编译器就会报错:无法转换

1.3 泛型的用途

泛型方法

在 java 中,泛型方法可以使用在成员方法、构造方法和静态方法中。语法如下:

public <申明泛型的类型> 类型参数 fun();如 public <T> T fun(T t);这里的 T 表示一个泛型类型,而 表示我们定义了一个类型为 T 的类型,这样的 T 类型就可以直接使用了,且 需要放在方法的返回值类型之前。T 即在申明的时候是不知道具体的类型的,只有的使用的时候才能明确其类型,T 不是一个类,但是可以当作是一种类型来使用。

泛型类

public class GenericClazz<T>{
    //这就是一个最基本的泛型类的样子
}

1.4 小结

泛型是计算机程序中一种重要的思维方式,它将数据结构和算法与数据类型相分离,使得同一套数据结构和算法能够应用于各种数据类型,而且可以保证类型安全,提高可读性。


2. 常用通配符

Pair<String, ? extends Number> p = new Pair<>("name",123);
System.out.println(p.getKey()+p.getValue());

常用的通配符为: T,E,K,V,?

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个 java 类型
  • K V (key value) 分别代表 java 键值中的 Key Value
  • E (element) 代表 Element

常用的通配符的三种形式

  • <?> 无限定的通配符。是让泛型能够接受**未知类型**的数据
  • < ? extends E>有上限的通配符。能接受指定类及其子类类型的数据,E就是该泛型的上边界
  • <? super E>有**下限**的通配符。能接受**指定类及其父类类型**的数据,E就是该泛型的下边界

2.1 理解通配符

  • 单独的 表示无限定通配符
  • ? extends Number 表示有限定通配符,表示插入Number的某个子类

不过,通配符形式更为简洁。虽然通配符形式更为简洁,但上面两种通配符都有一个重要的限制:只能读,不能写。

通配符的优点

  1. 它们的目的都是为了使方法接口更为灵活,可以接受更为广泛的类型。
  2. <? >和<? extends E>用于灵活读取,使得方法可以读取E或E的任意子类型的容器对象,它们可以用类型参数的形式替代,但通配符形式更为简洁。
  3. <? super E>用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象,它不能被类型参数形式替代。

2.2 总结

  • 通配符形式都可以用类型参数的形式来替代,通配符能做的,用类型参数都能做。
  • 通配符形式可以减少类型参数,形式上往往更为简单,可读性也更好,所以,能用通配符的就用通配符。
  • 如果类型参数之间有依赖关系,或者返回值依赖类型参数,或者需要写操作,则只能用类型参数。
  • 通配符形式和类型参数往往配合使用,比如,上面的copy方法,定义必要的类型参数,使用通配符表达依赖,并接受更广泛的数据类型。