Java核心技术第八章-泛型
摘要
本文根据《Java核心技术 卷一》一书的第八章总结而成,部分文章摘抄书内,作为个人笔记。
文章不会过于深入,望读者参考便好。
为什么要使用泛型程序设计
泛型程序设计(Generic programming) 意味着编写的代码可以被很多不同类型的对象所重用。
类型参数的好处
在没有泛型类之前,ArrayList类只维护一个Object引用的数组:
public class ArrayList {
private Object[] elementData; // 用于存放数据
public Object get(int i) { . . . }
public void add(Object o) { . . . }
...
}
问题
1.获取一个值时必须进行强制类型转换
2.这里没有错误检査。可以向数组列表中添加任何类的对象,如果数组的类型不一致,将 get 的结果进行强制强制类型,就会错误。
泛型提供了一个更好的解决方案: 类型参数:
ArrayList<String> array = new ArrayList<String>():
利用类型参数的信息,我们就可以在添加数据的时候保持类型统一,调用get方法时候也不需要进行强制类型转换,因为我们在初始化的时候就定义了类型,编译器识别返回值的类型就会帮我们转换该类型。
定义一个简单泛型类
public class Pair<T> {
private T num;
public Pair(T num) { this.num = num; }
public T getNum() { return num; }
public void setNum(T num) { this.num = num; }
}
我们可以看到,在Pair类名后面添加了一个,这个是泛型类的类型变量,而且还可以有多个类型变量,如
public class Pair<T,U> {
...
}
如果我们实例化Pair类,例如:
new Pair<String>;
那么我们就可以把上述的Pair类想象成如下:
public class Pair<String> {
private String num;
public Pair(String num) { this.num = num; }
public String getNum() { return num; }
public void setNum(String num) { this.num = num; }
}
是不是很简单呢?在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T、U、S表示任意类型。
泛型方法
定义一个带有类型参数的方法
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
可以看到类型变量(< T >)放在修饰符( public static )的后面,返回类型(T)的前面。泛型方法可以定义在普通类或泛型类中。
类型变量的限定
如果我们需要对类型变量加以约束,例如:传入的变量必须实现Comparable接口,因为需要该变量调用compareTo的方法。这样我们就可以使用extends关键字对变量进行限定。
public <T extends Comparable> T max(T a) {
a.compareTo(...);
...
}
无论变量需要限定为继承某个类或者实现某个接口,都是使用extends关键字进行限定。
泛型代码和虚拟机
类型擦除
无论我们在代码中怎么定义一个泛型类、泛型方法,都提供了一个相应的原始类型。原始类型的名字就是删去类型参数后的泛型类姓名。如 <T> 的原始类型为 Object,<T extends MyClass>的原始类型为MyClass。
代码就像下面这样:
public class Pair<T> {
private T property;
public Pair(T property) {
this.property = property;
}
}
类型擦除后:
public class Pair<Object> {
private Object property;
public Pair(Object property) {
this.property = property;
}
}
翻译泛型表达式
如果擦除返回类型,编译器会插入强制类型转换,就像下面这样:
Pair<Employee> buddies = . .
Employee buddy = buddies.getFirst();
擦除getFirst的返回类型后将返回Object类型,但是编译器将自动帮我们强制类型转换为Employee。
所以:编译器把这个方法执行操作分为两条指令:
对原始方法Pair.getFirst的调用
将返回的Object类型强制转换为Employee类型
小节总结:
虚拟机中没有泛型,只有普通的类和方法
所有的类型参数都用他们的限定类型替换
为保持类型安全性,必要时插入强制类型转换
桥方法被合成来保持多态(本文没有讲到,不过桥方法可以忽略,Java编写不规范才会有桥方法生成)
约束与局限性
不能用基本类型实例化类型参数
不可以用八大基本数据类型去实例化类型参数,你也没见过ArrayList<int>这样的代码吧。只有ArrayList<Integer>。原因是因为基本数据类型是不属于Object的。所以只能用他们的包装类型替换。
运行时类型查询只适用于原始类型
所有的类型查询只产生原始类型,因为在虚拟机没有所谓的泛型类型。
例如:
Pair<String> pair = new Pair<String>("johnson");
if (pair instanceof Pair<String>) {} // Error
if (pair instanceof Pair<T>) {} // Error
if (pair instanceof Pair) {} // Pass
不能创建参数化类型的数组
Pair<String>[] pairs = new Pair<String>[10]; //error
为什么不能这样定义呢?因pairs的类型是Pair[],可以转换为Object[],如果试图存储其他类型的元素,就会抛出ArrayStoreException异常,
pairs[0] = 10L; //抛异常
总之一句话,不严谨。所以不能创建参数化类型的数组。
不能实例化类型变量
不能使用 new T(...)、new T[...] 或 T.class。因为类型擦除后,T将变成Object,而且我们肯定不是希望实例化Object。
不过在Java8之后,我们可以使用Supplier<T>接口实现,这是一个函数式接口,表示一个无参数而且有返回类型为T的函数:
public class Pair<T> {
private T first;
private T second;
private Pair(T first, T second) {
this.first = first;
this.second = second;
}
public static <T> Pair<T> makePair(Supplier<T> constr) {
return new Pair<>(constr.get(), constr.get());
}
public static void main(String[] args) {
Pair<String> pair = Pair.makePair(String::new);
}
}
泛型类中的静态上下文中类型变量无效
不能在静态域或方法中引用类型变量。例如:
public class Pair<T> {
private static T instance; //Error
public static T getInstance() { //Error
if (instance == null)
instance = new Pair<T>();
return instance;
}
}
因为类中的类型变量(<T>)是在对象中的作用域有效,而不是在类中的作用域有效。如果要使用泛型方法,可以参照文章上面的泛型方法哦~
不能抛出或捕获泛型类的实例
即不能抛出也不能捕获泛型类的对象,甚至扩展Throwable都是不合法的:
public class Pair<String> extend Exceotion {} //Error
public static <T extends Throwable> void doWork(Class<T> t) {
try {
...
} catch (T e) { //Error
...
}
}
但是在抛出异常后对异常使用类型变量是允许的(个人感觉没见过这样的代码)。
public static <T extends Throwable> void doWork(Class<T> t) throw T { //Pass
try {
...
} catch (Exception e) {
throw e;
}
}
泛型类型的继承规则
如Manager类继承Employee类。但是Pair<Employee>和Pair<Manager>是没有关联的。就像下面的代码,就会提示报错,传递失败:
Pair<Manager> managerPair = new Pair<Manager>();
Pair<Employee> employeePair = managerPair; //Error
通配符类型
通配符概念
通配符类型中,允许类型参数变化,使用 ? 标识通配符类型:
Pair<? extends Employee>
若Pair类如下
public class Pair<T> {
private T object;
public void setObject(T object) {
this.object = object;
}
}
那么使用通配符可以解决泛型类型的继承规则问题,如:
Pair<Manager> managerPair = new Pair<Manager>();
Pair<? extends Employee> employeePair = managerPair; //Pass
Manager manager = new Manager();
employeePair.setObject(manager); //Error
使用<? extends Employee>,编译器只知道employeePair 是Employee的子类,但是不清楚具体是什么子类,所以最后一步employeePair.setObject(manager)不能执行。
通配符的超类型限定
通配符还有一个附加的能力,就是可以指定一个超类型限定,如:
public class Pair<T> [
...
public static void salary(Pair<? super Manager> result) {
//...
}
}
<? super Manager>这个通配符为Manager的所有超类型(包含Manger),例如:
Pair<Manager> managerPair = new Pair<Manager>();
Pair<Employee> employeePair = new Pair<Employee>();
Pair.salary(managerPair); //Pass
Pair.salary(employeePair); //Pass
// 假如 ManagerChild为Manager子类
Pair<ManagerChild> managerChildPair = new Pair<ManagerChild>();
Pair.salary(managerChildPair); //Error
无限定通配符
无限定通配符,如:Pair<?>,当我们不需要理会他的实际类型时候,就可以使用无限定通配符,上代码:
public static boolean hasNull(Pair<?> pair) {
return pair.getObject() == null;
}
说实话,通配符搞得我头昏脑胀的,反复不断地看文章,才开始慢慢看懂,我太难了。。。
文章到这里就结束啦,不知道各位小伙伴看懂了没,没看懂的话可能是我的功底和文章写作能力还有待提高,小伙伴们也可以去看一下《Java核心技术 卷一》这本书呢,感觉还是挺不错的。最近把这本书捡起来看也是发现基础是非常重要的,先把基础沉淀好了,再学习其他的技术点也会更容易入手,也会知其然知其所然。最近非常喜欢的一句话,送给大家:“万丈高楼平地起,勿在浮沙筑高台”。
个人博客网址: https://colablog.cn/
如果我的文章帮助到您,可以关注我的微信公众号,第一时间分享文章给您

Java核心技术第八章-泛型的更多相关文章
- Java核心技术第八章——泛型程序设计(1)
1.泛型程序设计 泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用.例如:不希望为了聚集String和Integer对象分别设计不同的类.(个人觉得此处说的聚集译为:创建一个对象,属性可以为 ...
- Java核心技术点之泛型
1. Why ——引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过了一阵,我们想要实现一个大小 ...
- Java核心技术点之集合框架
1. 概述 Java集合框架由Java类库的一系列接口.抽象类以及具体实现类组成.我们这里所说的集合就是把一组对象组织到一起,然后再根据不同的需求操纵这些数据.集合类型就是容纳这些对象的一个容 ...
- 读《java核心技术卷一》有感
过去一个多月了吧.才囫囵吞枣地把这书过了一遍.话说这书也够长的,一共706页.我从来不是个喜欢记录的人,一直以来看什么书都是看完了就扔一边去,可能有时候有那么一点想记录下来的冲动,但算算时间太紧,很多 ...
- java核心技术卷一
java核心技术卷一 java基础类型 整型 数据类型 字节数 取值范围 int 4 +_2^4*8-1 short 2 +_2^2*8-1 long 8 +_2^8*8-1 byte 1 -128- ...
- 对《Java核心技术卷一》读者的一些建议
<Java核心技术卷一>是唯一可以和<Java编程思想>媲美的一本 Java 入门书.单从技术的角度来看,前者更好一些.但上升到思想层面嘛,自然后者更好,两者的偏重点不同. 思 ...
- 小白学Java:老师!泛型我懂了!
目录 小白学Java:老师!泛型我懂了! 泛型概述 定义泛型 泛型类的定义 泛型方法的定义 类型变量的限定 原生类型与向后兼容 通配泛型 非受限通配 受限通配 下限通配 泛型的擦除和限制 类型擦除 类 ...
- 《Java核心技术卷I》观赏指南
Tomxin7 如果你有想看书的计划,但是还在纠结哪些书值得看,可以简单看看"观赏指南"系列,本文会简单列出书中内容,给还没有买书的朋友提供一个参考. 前言 秋招过去很久了,虽然在 ...
- 【阅读笔记】Java核心技术卷一 #0
这是一篇备忘性质的读书笔记,仅记录个人觉得有用的知识点 本文作为一个目录索引,部分章节跳过 吐槽:此书中文翻译有不少地方不太通顺,这种情况我要把英文版对应的部分也读一遍才能明白(说实话,英文里的从句表 ...
随机推荐
- vue运行报错error:Cannot assign to read only property 'exports' of object '#<Object>'
用weex做项目的时候,npm start 之后一直报错error:Cannot assign to read only property 'exports' of object '#<Obje ...
- 基于verdaccio的npm私有仓库搭建
详见个人博客:https://shengchangwei.github.io/verdaccio/ 一.使用npm安装 npm install --global verdaccio 二.cmd 启动 ...
- [考试反思]1105csp-s模拟测试102: 贪婪
还是有点蠢... 多测没清空T3挂40...(只得了人口普查分20) 多测题要把样例复制粘两遍自测一下防止未清空出锅. 然而不算分... 其实到现在了算不算也不重要了吧... 而且其实T3只考虑最长路 ...
- [考试反思]0820NOIP模拟测试27:幻影
注:某让我把“傻孩子”三个字全部删掉了语法不通之处自行脑补(这句本身就语法不通) skyhAK 我和以及milk_feng220 还真的没有考虑过如果我考前3的话这个颜色该怎么表示(自从不粘排行榜以来 ...
- [考试反思]0814NOIP模拟测试21
前两名是外校的240.220.kx和skyh拿到了190的[暴力打满]的好成绩. 我第5是170分,然而160分就是第19了. 在前一晚上刚刚爆炸完毕后,心态格外平稳. 想想前一天晚上的挣扎: 啊啊啊 ...
- NOIP模拟 35
int乘爆见祖宗 难得地T3暴力打满 T1T2思路也都正确 然而T2没看出一个int乘爆直接滚粗.. 这谁抗的住啊... 跟天皇说我差点就rank1了 换来的只有一张奸笑的大脸和一堆垃圾的嘲讽 真巨啊 ...
- NOIP模拟 14
垃圾成绩,一点都不稳定. 如果把数组开小的分得到的话..总分还挺不错.. 那又能怪谁,都快NOIP了还犯这种傻逼错误 nc哥是要阿卡的节奏..真是太强了 某kyh也不知道偷了谁的rp,分高的一批 wd ...
- MongoDB 谨防索引seek的效率问题
目录 背景 初步分析 索引seeks的原因 优化思路 小结 声明:本文同步发表于 MongoDB 中文社区,传送门: http://www.mongoing.com/archives/27310 背景 ...
- 2019.NET Conf China(中国.NET开发者峰会)活动全纪录:.NET技术之崛起,已势不可挡
一.微软走向开放与.NET Core的诞生 当今时代,气象更新,技术飞速发展. 当今时代,开发者大概是最优秀的群体.每一位开发者,无不奋勇向前,努力追寻时代的步伐,以大无畏的精神迎接挑战,紧跟大时代成 ...
- PCA降维的原理、方法、以及python实现。
PCA(主成分分析法) 1. PCA(最大化方差定义或者最小化投影误差定义)是一种无监督算法,也就是我们不需要标签也能对数据做降维,这就使得其应用范围更加广泛了.那么PCA的核心思想是什么呢? 例如D ...