Java泛型的基本介绍与使用
为什么要使用泛型?
在Java中增加泛型之前,泛型程序设计是用继承来实现的,例如ArrayList,只维护Object引用的数组:
public class ArrayList{
private Object[] elementData; public Object get(int i){...}
public void add(Object o){...}
}
在这里有一个问题,就是每次使用的时候,都必须进行一次强制类型转换
ArrayList list = new ArrayList();
...
String name = (String)list.get(0);
可以发现还没有错误检查,如果是其他类型会检测不到,当添加的时候,可以是任何类的对象
每次都转换一次很麻烦,用什么解决呢,类型参数!
ArrayList<String> list = new ArrayList<String>();
//在JAVASE 7以后的版本中,可以直接使用一下方法来声明了
ArrayList<String> list = new ArrayList<>();
简单泛型类
public class Pair<T>{
private T first;
private T second; public Pair(){first = null; second = null;}
public Pair(T first, T second){this.first = first; this.second = second} //get set
...
}
现在,我们可以像现在这样定义了
Pair<String> test1 = new Pair<>();
Pair<Integer> test2 = new Pair<>();
泛型方法
public class test{
public static <T> T first(T...a){
//...
}
}
但是需要注意的是,下面这几种,并不是泛型方法,不要混淆
public class test<T>{
private T t; //这个不是,只是一个普通的成员的方法
public test(T t){
this.t = t;
}
//这个也不是,只是泛型类是形参
public void add(test<T> object){...}
}
通配符
通配符是什么,为什么要使用通配符?试想一下,如果我们在一个方法中要传入的参数可能是一个类的子类,这该怎么办呢?
public void test(Persion<Number> p){...} x.test(Intger); //错误
x.test(Double);
不妨自己在编译器上试一试,这里是识别不了的,泛型中这两个没有关联关系,so,可以使用通配符
public void test(List<? extends T>){...}
这样子类和父类就联系起来了,?是类型实参,不是类型形参
如果像上面这样使用,我们可以add元素进去吗?反着思考一下,一个父类派生出很多个子类,我们在实例化的时候,很可能new出来的不是一个东西,这可不行呀,要存就得存一种,哪有Int,float都存进去的道理,如果还不明白可以看看代码
List<? extends Person> = new ArrayList<Student>();
List<? extends Person> = new ArrayList<Teacher>();
这样的话我们只能Get,并不能add,想要add那我们现在可以这样
public void test(List<? superT>){...}
如此之后,只能add,不能get,又是为什么呢,还是反着思考,既然我们能够add元素进去,但是每一个元素的实际类型不相同
List<? super Student> = new ArrayList<Student>();
List<? super Student> = new ArrayList<Person>();
当我们想get到Student的时候,可能是一个Person类型,这个Person可能是个Teacher
说了这么多,终于到了类型擦除
类型擦除
在JVM中不存在什么泛型,只有基本的类,当我们定义了一个泛型类的时候
public class Node<T> {
private T data;
private Node<T> next; public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
} // ...
}
在做完类型检查后,会变成这样
public class Node{
private Object data;
private Node next; public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
// ...
}
惊不惊喜意不意外!如果我们不想变成Object怎么办,可以自己设置
public class Node<T extneds Comparable<T>> {
private T data;
private Node<T> next; public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
// ...
} public class Node {
private Comparable data;
private Node next; public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
// ...
}
看似其实就是这样嘛!但是这样的话会引起一些问题
1、不允许创建泛型数组
因为数组中的元素必须统一类型,如若使用了泛型数组,类型都被擦除成Object后,我们不会知道插入的数据是否都是同一个类型的,出错很难排查,可以运行一下下面的代码
Class c1 = new ArrayList<Integer>().getClass();
Class c2 = new ArrayList<Double>().getClass();
System.out.println(c1 == c2);
最终会得到true的结果,这里可以联想到,泛型无法使用instanceof
2、桥方法的合成用来保持多态
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
类型擦除后
public class Node {
public Object data;
public Node(Object data) { this.data = data; }
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
MyNode mn = new MyNode(5);
Node n = mn;
n.setData("Hello");
实际上不是这样的,这会抛出ClassCastExeption,在哪里?
class MyNode extends Node {
// 桥方法
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}
setData方法里面有一个强制类型转换,String没办法转换成Integer,记住一句话,桥方法被合成用来保持多态
3、反射和泛型
反射允许你在运行时分析任意的对象,如果对象是泛型类的实例,关于泛型类型参数则得不到太多信息,因为它们会被擦除
最新文章
- Configure a VLAN on top of a team with NetworkManager (nmcli) in RHEL7
- 显示oracle表的分区信息
- xcode意外退出
- vs2015 附加到进程找不到w3wp.exe
- WPF入门教程系列五——Window 介绍
- 从C#到Objective-C,循序渐进学习苹果开发(5)--利用XCode来进行IOS的程序开发
- 160918、BigDecimal运算
- 2016年11月5日 星期六 --出埃及记 Exodus 19:21
- [BZOJ 4103] [Thu Summer Camp 2015] 异或运算 【可持久化Trie】
- win8 开启wifi
- poj 2431 Expedition 贪心
- 错误解决--oracle中出现ORA-01791: 不是 SELECTed 表达式 错误
- Visual Assist X 快捷键
- android 优化
- nodejs之socket.io模块——实现了websocket协议
- Java基础语法-Lambda表达式
- VBS弹出来的对话框如何置顶!--果然技巧
- 第42章:MongoDB-集群--Sharding(分片)--单机的搭建
- loj#2054. 「TJOI / HEOI2016」树
- 手机号的 AES/CBC/PKCS7Padding 加解密
热门文章
- (第六场)Singing Contest 【模拟】
- 【luogu P3275 [SCOI2011]糖果】 题解
- 【luogu P1186 玛丽卡】 题解
- o&#39;Reill的SVG精髓(第二版)学习笔记——第一章
- linux简介及虚拟机安装
- 与select2有关的知识点总结
- 分组函数group by和Oracle中分析函数partition by的用法以及区别
- IOS NSNotification 通知
- export default 和 export的使用方式
- 邮件发送失败问题:Sending the email to the following server failed : smtp.qiye.163.com:25