⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 https://www.imooc.com/article/276201 「allen4tech」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

泛型基础

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时的类型安全检测机制。该机制允许程序员在强类型程序设计语言中编写代码时,使用一些以后才指定的类型,在实例化时作为参数指明这些类型。

下面看一个简单的例子,如果我们需要创建一个只包含字符串的集合,没有泛型,我们会这样做

ArrayList strList = new ArrayList();
strList.add("a");
strList.add("b");
strList.add(1);

即使在其中错误的插入了一个整型,依然可以顺利通过编译。若要取集合中的值,需要进行强制类型转换,

String str = (String)strList.get(2);

此时由于集合中第 3 个参数为整型,而非字符串型,在运行时会报出转换错误,

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

这样我们很难保证集合中的元素都是我们想要的类型。有了泛型之后,我们可以利用编译器的类型检测机制来帮助我们去发现类型是否合法。

ArrayList<String> strList2 = new ArrayList<>();
strList2.add("a");
strList2.add("b");
strList2.add(1);

同样的在集合中插入了一个整型的值,此时编译器会帮助我们发现,值 1 不是 String 类型,无法插入 strList2 集合,编译器会报出错误,

add(java.lang.String) in ArrayList cannot be applied to (int)

这样就很好的帮助我们避免了一些开发过程中可能出现的低级错误。

自定义泛型

自定义泛型类型

JAVA 本身为我们设计了一些泛型类型,如 ArrayList, HashMap<K, V> 等,我们也可以根据实际业务需求,自定义我们自己的泛型类型。

以下我们以一个简单的例子演示一下如何自定义一个泛型类。假设我们现在有一个约会类 dating

public class Dating<T> {
private T person;

public Dating(){
this.setPerson(null);
}
public Dating(T person){
this.setPerson(person);
}

public T getPerson() {
return person;
}
public void setPerson(T person) {
this.person = person;
}
}

T 为类型变量,用 <> 包裹,放在类名后面,这样我们可以在实例化 Dating 类的时候,给它传入合适的类型参数。如果是程序猿,我们可以限制只能传入女神类,如果是程序媛,我们可以限制只能传入男神类,没有彭于晏?我们自己 new 一个,没有贾静雯?我们有 new!

Dating<Man> dating = new Dating<>();
Man pengYuYan = new Man("彭于晏");
dating.setPerson(pengYuYan);

Dating<Woman> dating2 = new Dating<>();
Woman jiaJingWen = new Woman("贾静雯");
dating2.setPerson(jiaJingWen);

程序猿尝试约一下彭于晏?

dating2.setPerson(pengYuYan);

程序报错了:setPerson (Woman) in Dating cannot be applied to (Man),用中文翻译一下就是:性别不合。很好的杜绝了见面后发现对方也带棍的尴尬。

类型变量的限制

对于程序媛,约会几次后可能会从各方面考察对方是否符合自己的标准,首当其冲的是赚钱能力,如果对方无赚钱能力,只是个吃软饭的小白脸,则不会再有下次约会,可以对约会对象做一个限制,只有有赚钱能力的人才会和他有下次约会。

我们先定义一个接口

public interface MakeMoney {
String makeMoney();
}

然后对 Dating 类的类型参数加一个限制

public class Dating<T extends MakeMoney> {
private T person;
...
}

观察类名之后的 T 变为了 T extends MakeMoney, 此处限制了传入的类型,必须实现了 MakeMoney 接口,否则编译时则报错。(MakeMoeny 也可以是类,无论是类还是接口,都使用 extends 而不是 implements

我们的 Man 类型并没有实现 MakeMoney 接口,当将一个 Man 实例传入 dating 对象,则

Man toyBoy = new Man();
Dating dating = new Dating(toyBoy);

编译错误:Dating (MakeMoney) in Dating cannot be applied to (Man),翻译成中文就是:死开,小白脸!

自定义泛型方法

经过几次约会后,程序猿(程序媛)和女(男)神情意相投,决定结婚,下面新建一个婚礼类 Wedding,

public class Wedding {
private Man bridegroom;
private Woman bride;

public Wedding(Man bridegroom, Woman bride) {
this.bridegroom = bridegroom;
this.bride = bride;
}

public Man getBridegroom() {
return bridegroom;
}

public void setBridegroom(Man bridegroom) {
this.bridegroom = bridegroom;
}

public Woman getBride() {
return bride;
}

public void setBride(Woman bride) {
this.bride = bride;
}

public <T> void receivingGift(T gift){
System.out.println(gift);
}
}

receivingGift 方法为一个泛型方法,可在调用方法时指定要传入的参数类型,同样的用 T 表示变量类型,用 <> 包裹,放在返回类型前。

婚礼最哈皮的莫过于收礼了,亲朋好友们一个个都很踊跃,有的送红包, 有的送超跑。

Man myself = new Man();
Woman jiaJingWen = new Woman();
Wedding wedding = new Wedding(myself,jiaJingWen);
wedding.<RedPacket>receivingGift(new RedPacket());
wedding.<Car>receivingGift(new Car());

以上为泛型方法的调用方式,在方法名前用 <> 包裹参数类型。来看一看执行结果:

图片描述

泛型方法同样也可以存在于泛型类中,那泛型类定义的类型参数 T 和泛型类中的泛型方法定义的类型 T 是否需要是同一类型呢?给之前的 Dating 类添加一个泛型方法

public class Dating<T extends MakeMoney> {
...
public <T> T Meet(T person){
return "来了,老弟!";
}
}

我们来实例化一个 Dating 类,并设置约会对象为林志玲,后由于不可描述的原因,放了女神鸽子,去见了石榴姐。

Rich linZhiLing = new Rich();
linZhiLing.setName("林志玲");
Dating<Rich> dating = new Dating<>();
dating.setPerson(linZhiLing);

Woman poor = new Woman();
poor.setName("石榴姐");
Woman woman = dating.Meet(poor);
System.out.println(woman);

Rich 类实现了 MakeMoney 接口,所以可以作为参数传入 Dating 类,Man 没有实现 MakeMoney 接口,前面的例子已经演示了,传入 Dating 类会报错,但是调用 Meet 方法时将 Man 的实例传入,就可以编译通过,可见泛型方法定义的 T 和泛型类定义的 T 没有任何关系。所以通常为了避免混淆,我们会用不同字母来区别泛型类的类型参数和泛型方法的类型参数。

public class Dating<T extends MakeMoney> {
...
public <E> E Meet(E person){
return "来了,老弟!";
}
}

OK, 整个意淫过程告一段落,成功迎娶白富美,走上人生巅峰!

后续介绍

JAVA 泛型意淫之旅(二)

  1. 编译器如何处理泛型
  2. 集合与泛型
  3. 泛型中的继承关系
  4. 泛型中的约束
  5. 泛型中的类型通配符
  6. 阿里巴巴泛型使用规范

JAVA 泛型意淫之旅(三)

  1. 常见面试题
文章目录
  1. 1. 泛型基础
  2. 2. 自定义泛型
    1. 2.1. 自定义泛型类型
    2. 2.2. 类型变量的限制
    3. 2.3. 自定义泛型方法
  3. 3. 后续介绍
    1. 3.1. JAVA 泛型意淫之旅(二)
    2. 3.2. JAVA 泛型意淫之旅(三)