解析令人费解的java泛型
对于我们java中的泛型,可能很多人知道怎么使用并且使用的还不错,但是我认为想要恰到好处的使用泛型,还是需要深入的了解一下它的各种概念和内部原理。本文将尽可能的囊括java泛型中的重要的概念。主要内容如下:
- 泛型的定义及为什么要使用泛型
- 定义一个简单的泛型类
- 定义一个简单的泛型方法
- 类型参数的限定
- 泛型内部实现的基本原理
- 泛型通配符(难点)
- 泛型的其他实现细节
**一、何谓泛型**
泛型程序设计意味着编写的代码可以被不同中类型的对象重用。例如:MyList<T>,MyList<Integer>是一种类型,MyList<String>也是一种类型,但是使用的代码块都是MyList<T>,这也就是java中引入泛型的一种原因:可以增强代码的复用性,当然这种限定死类型的方式也会使得代码的安全性和可读性更高。
**二、一个简单的泛型类**
先看一个完整的泛型类:
```
/*一个简单的泛型类的声明如下*/
public class Pair {
private T a;
private T b;
public Pair(T a, T b){
this.a = a;
this.b = b;
}
}
```
由此可以看出来,泛型类型和普通类型的区别主要在于:类名之后多了个<T>,并且实例域类型可以不是具体的类型而是不确定的T类型。其中,我们管T叫做类型变量,类型变量一般使用大写字母表示并且很短(在java中使用E表示集合的元素类型,K和V分别表示关键字和值的类型)。
使用具体的类型来替换类型变量的过程我们叫做实例化泛型类型。例如:Pair<Integer>,<Double>等。这其中需要注意的是:java中的9中基本类型是不能作为类型变量的,也就是:Pair<int>,是会不允许的。当然,声明一个泛型类时,不局限于一个类型变量,可以由多个类型变量,例如:
```
/*声明两个类型变量也是可以的*/
public class Pair {
private T a;
private U b;
public Pair(T a, U b){
this.a = a;
this.b = b;
}
}
//Pair p new Pair("abc",12);
//创建泛型类实例变量的时候,可以省略类型变量,编译器可以推测出来
```
**三、一个简单的泛型方法**
怎么定义泛型类,我们已经介绍过了,接下来我们一起看看泛型方法是如何定义和调用的。
```
/*泛型类中定义了一个泛型方法*/
public class Pair {
//声明一个泛型方法
public T getA(T c){
return c;
}
}
/*main函数中调用泛型方法*/
public class Test2 {
public static void main(String[] args){
Pair p = new Pair(1,2);
//调用泛型方法
System.out.println(p.show(10));
}
}
```
我们可以看到,声明一个泛型方法:public <T > T getA(T c),<T >放在返回值前面,修饰符后面,T表示返回类型。泛型方法的调用:p.<Integer>show(10),在方法名前面放置类型变量,当然也可以选择省略,当编译器没有足够的信息推测出来时就会报错,那时你再添加也不迟(但是,如果你能减轻计算机的工作的话,想必是可以提高效率的)
小结一下,泛型类和泛型方法。泛型类中可以声明泛型方法也可以声明普通方法,泛型方法可以出现在泛型类中也可以出现在普通类中,也就是它们之间并没有什么约束关系。
**四、类型变量的限定**
前面我们已经知道了什么是类型变量,我们看一段代码:
```
public class Pair {
public static int myCompare(T a,T b){
return a.compareTo(b);//此处编译不通过
}
}
```
我们知道,如果想要使用compareTo方法,就要实现Comparable接口,或者继承实现了此接口的类。此处想要使得程序正确,有两种办法。第一种:使类继承Comparable接口并且实现compareTo方法。第二种:就是使用类型变量限定。如下:
```
/*限定变量类型*/
public class Pair {
public static int myCompare(T a,T b){
return a.compareTo(b);
}
}
```
细心的同学可能已经发现,相比于原来的方法,就是使类型变量继承与Comparable接口。原来的<T >变成了<T extends Comparable>,表示:原来的T可以是任意类型的,而现在的T被限制必须实现了Comparable 接口,就是说,凡是使用此泛型的类都是直接或者间接继承了Comparable 接口并实现其中方法的。所以,一旦我们将T限定了,就不用考虑实现Comparable 接口的事情了,程序的封装性更强了。
对类型变量的限定可以由多个限定,它们之间使用&分隔,而使用逗号分隔类型变量。看个例子:
```
//一个类型变量的一个类型限定
//一个类型变量的两个类型限定
//两个类型变量的类型限定
```
**五、泛型实现的基本原理**
讨论了这么多的泛型方法,泛型类以及各种使用技巧,接下来,我们一起看看虚拟机实际执行时是怎么对待我们的泛型的。我们都知道java中有编译器和虚拟机,但实际上我们的泛型在这两者看来是不一样的,也就是说,虚拟机是不认识泛型的,而只有我们强大的编译器是认识泛型的。那他们是怎么实现统一的呢?接下来我们详细来看。
在java中,无论何时定义了一个泛型,它都会自动生成一个相应的**原始类型**。我们叫这个过程为:类型擦除。例如下面的代码:
```
/*这是一段泛型类的代码*/
public class Pair {
private T a;
private T b;
public T getA(){
return this.a;
}
public T getB(){
return this.b;
}
}
```
```
经过类型擦除之后生成原始类型:
```
```
public class Pair{
private Object a;
private Object b;
public Object getA(){
return this.a;
}
public Object getB(){
return this.b;
}
}
```
经过对比,我们可以得出结论:去除了泛型的标志性符号<>并且所有的T类型都被替换成Object类型了。难道我们的类型擦除就是将所有的未知类型转换为Object类型吗?当然不是,类型擦除是有规则的而不是一味的将未知类型T转换成Object类型的。
对于有限定的类型变量就将用类型变量的第一个限定类型替换。如:Pair<T extends Comparable & Serializable>,就会选择用Comparable替换所有的T并去除修饰在类后面的泛型符号,生成原始类型。
对于没有限定类型的类型变量就默认使用Object替换类型变量。例如:Pair<T>就会使用Object替换所有的T类型变量。
最后小结一下,类型擦除针对是否有类型限定类型,根据不同的状况进行替换生成相应的原始类型供jvm调用。
未完,待续。。。。
解析令人费解的java泛型的更多相关文章
- 令人费解的java泛型
对于我们java中的泛型,可能很多人知道怎么使用并且使用的还不错,但是我认为想要恰到好处的使用泛型,还是需要深入的了解一下它的各种概念和内部原理.本文将尽可能的囊括java泛型中的重要的概念 ...
- Java泛型解析(03):虚拟机运行泛型代码
Java泛型解析(03):虚拟机运行泛型代码 Java虚拟机是不存在泛型类型对象的,全部的对象都属于普通类,甚至在泛型实现的早起版本号中,可以将使用泛型的程序编译为在1.0虚拟机上可以执行的 ...
- Java泛型解析(04):约束和局限性
Java泛型解析(04):约束和局限性 前两节.认识和学习了泛型的限定以及通配符.刚開始学习的人可能须要一些时间去体会到泛型程序设计的优点和力量,特别是想成为库程序猿的同学就须要下 ...
- Java泛型解析(01):认识泛型
Java泛型解析(01):认识泛型 What Java从1.0版本号到如今的8.中间Java5中发生了一个非常重要的变化,那就是泛型机制的引入.Java5引入了泛型,主要还是为了满足在199 ...
- Java泛型解析(02):通配符限定
Java泛型解析(02):通配符限定 考虑一个这种场景.计算数组中的最大元素. [code01] public class ArrayUtil { public static <T&g ...
- 解析java泛型(一)
对于我们java中的泛型,可能很多人知道怎么使用并且使用的还不错,但是我认为想要恰到好处的使用泛型,还是需要深入的了解一下它的各种概念和内部原理.本文将尽可能的囊括java泛型中的重要的概念.主要内容 ...
- 解析java泛型(二)
上篇我们简单的介绍了java中泛型的最基本的内容,知道了什么是泛型以及泛型对我们的程序编写有什么好处,最后一类型限定收尾.本篇将从类型限定开始阐述java泛型中很重要的概念:通配符 一.何为通配符 ...
- 【Java心得总结四】Java泛型下——万恶的擦除
一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...
- 【Java心得总结三】Java泛型上——初识泛型
一.函数参数与泛型比较 泛型(generics),从字面的意思理解就是泛化的类型,即参数化类型.泛型的作用是什么,这里与函数参数做一个比较: 无参数的函数: public int[] newIntAr ...
随机推荐
- <!DOCTYPE> 声明 引发的错误
<!DOCTYPE> 声明必须是 HTML 文档的第一行,位于 <html> 标签之前. 在写模板的时候,因为最近开始给每个文件添加注释,无意中将注释写在文件的第一行.导致页面 ...
- kmp算法理解与记录
字符串匹配的暴力解法 给定字符串s和p,寻找字符串p在字符串s中出现的位置,暴力解法如下所示: 如果当前字符匹配成功,++i;++j,继续匹配下一字符. 如果s[i]与s[j]匹配失败,令i-=(j- ...
- bzoj1355——2016——3——15
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1355 题目大意: 1355: [Baltic2009]Radio Transmission ...
- yum命令被锁 Existing lock /var/run/yum.pid
有时使用yum命令,不知怎会回事就提示: Existing lock /var/run/yum.pid: another copy is running as... 感觉也没操作什么错误的事情. 此时 ...
- 五 Android Capabilities讲解
1.Capabilities介绍 可以看下之前代码里面设置的capabilities DesiredCapabilities capabilities = new DesiredCapabilitie ...
- 负载均衡探测器lbd
负载均衡探测器lbd 大型网站为了解决海量访问问题,往往采用负载均衡技术,将用户的访问分配到不同的服务器上.网站的负载均衡可以从DNS和HTTP两个环节进行实施.在进行Web渗透测试的时候,需要先 ...
- BNU Online Judge-34973-Liserious战队
题目链接 http://www.bnuoj.com/bnuoj/problem_show.php?pid=34973 题目不难,很容易,不过不仔细看题可能你一直做不出,注意<<一共分为1~ ...
- Flex移动应用程序开发的技巧和窍门(四)
范例文件 flex-mobile-dev-tips-tricks-pt4.zip 这是本系列文章的第四部分,该系列文章涵盖Flex移动开发的秘诀与窍门. 第一部分关注切换视图以及切换执行应用时的数据处 ...
- spring EL表达式,null-safe表达式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://ww ...
- Memcached在windows下的安装和使用
1.下载memcached安装文件及c#开发所需的dll 2.解压<memcached-1.2.6-win32-bin.zip>,并cmd,定位到解压目录. 3.安装服务:输入命令 mem ...