一 前言:初识泛型

废话不说,先来看一段代码:

public class Holder {

private Object data;

public Holder(Object data ){

this.data = data;

}

public void setData(Object data) {

this.data = data;

}

public Object getData() {

return data;

}

public static void main(String[] args){

Holder holder = new Holder(new SomeNode());

SomeNode someNode = holder.getData();

}

}

class SomeNode{}

Holder类是一个容器,它的作用是用来保存其他类的,这里我们用它来保存SomeNode类,随后把它取出来,编译运行,结果如下:

Error:(21, 43) java: incompatible types

required: SomeNode

found: java.lang.Object

意思是,需要的是SomeNode,取出来的却是Object,如此看来,如果我想保存SomeNode类,就只能把data声明为SomeNode:

private SomeNode data;

这就意味着我们需要为每一个类创造一个Holder,这肯定是不行的,于是泛型的作用来了,泛型,可以理解为任何类型,意思是我可以声明一个可以容纳任何类型的容器:

public class Holder {

private T data;

public Holder(T data ){

this.data = data;

}

public void setData(T data) {

this.data = data;

}

public T getData() {

return data;

}

public static void main(String[] args){

Holder holder = new Holder(new SomeNode());

SomeNode someNode = holder.getData();

}

}

class SomeNode{}

注意写法,在类声明后面加个就行了,你也可以加,只是一个占位符,形参而已。然后我们再把它取出来:

Process finished with exit code 0

程序没有报错,如果这时候我们使用holder的set()方法去插入设置一些非SomeNode类型的值,代码如下:

public static void main(String[] args){

Holder holder = new Holder(new SomeNode());

SomeNode someNode = holder.getData();

holder.setData("AAAA");

}

看结果:

Error:(22, 15) java: method setData in class Holder cannot be applied to given types;

required: SomeNode

found: java.lang.String

reason: actual argument java.lang.String cannot be converted to SomeNode by method invocation conversion

泛型机制就自动为我们报错,很方便。

二 泛型类:元组(Tuple),返回多个对象

熟悉python的同学都知道元组的概念,它是一个只读列表,在返回多个结果时是很有用的,我们利用泛型特性来创造一个包含两个对象的元组:

public class Tuple {

public static void main(String[] args){

TwoTuple t = new TwoTuple("Monkey",12);

System.out.println(t.toString());

}

}

class TwoTuple{

final A first;

final B second;

public TwoTuple(A a,B b){

first = a;

second = b;

}

public String toString(){

return "("+first+","+second+")";

}

}

来看结果:

(Monkey,12)

是不是很方便:)如果想要一个长度为3的元组可以这么写:

public class Tuple {

public static void main(String[] args){

ThreeTuple t = new ThreeTuple("Dog",12,true);

System.out.println(t.toString());

}

}

class TwoTuple{

final A first;

final B second;

public TwoTuple(A a,B b){

first = a;

second = b;

}

public String toString(){

return "("+first+","+second+")";

}

}

class ThreeTuple extends TwoTuple{

final C three;

public ThreeTuple(A a,B b,C c){

super(a,b);

three = c;

}

public String toString(){

return "("+first+","+second+","+three+")";

}

}

结果如下:

(Dog,12,true)

三 泛型接口

泛型接口的定义和泛型类的定义类似,我们来定义一个生成器接口:

public interface Generator {

T next();

}

接着我们实现这个接口,来生成斐波拉契数:

public class Fib implements Generator {

private int count = 0;

@Override

public Integer next() {

return fib(count++);

}

private int fib(int n){

if (n<2)

return 1;

else

return fib(n-2) + fib(n-1);

}

public static void main(String[] args){

Fib f = new Fib();

for (int i=0;i<100;i++){

System.out.println(f.next());

}

}

}

四 泛型方法

比起泛型类,我们更推荐去使用泛型方法,泛型方法定义起来也很简单,我们只需将泛型参数放在返回类型前面即可:

public class GenericMethods {

public void f(T x){

System.out.println(x.getClass().getName());

}

public static void main(String[] args){

GenericMethods g = new GenericMethods();

g.f("Hello");

g.f(100);

g.f(true);

}

}

这里我们定义了一个泛型方法f(),并使用getClass获取类的相关信息(关于Class对象的知识点这里),来看结果:

java.lang.String

java.lang.Integer

java.lang.Boolean

这里还要注意一下Varargs(变长参数)机制和泛型的结合:

public class GenericVarargs {

public static List makeList(T...args){

List list = new ArrayList();

for (T item : args){

list.add(item);

}

return list;

}

public static void main(String[] args){

List list = makeList("A","B","C","D");

System.out.println(list);

}

}

结果如下:

[A, B, C, D]

六 类型擦除

在认识类型擦除之前,我们首先要明白编译器对泛型的处理有两种方式:

1.Code specialization

在实例化一个泛型类或者泛型方法是都生成一份新的字节码,比如对于List,List,List产生三份不同的字节码。

2.Code sharing

对每个泛型类只生成唯一的一份目标代码;该泛型类的所有实例都映射到这份目标代码上,在需要的时候执行类型检查和类型转换。参考文章

C++的模板是典型的Code specialization实现,而Java泛型则是Code sharing实现,将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。对擦除更通俗的理解就是:编译器生成的bytecode是不包涵泛型信息的。我们看下面的代码:

public class ErasedType {

public static void main(String[] args){

Class c1 = new ArrayList().getClass();

Class c2 = new ArrayList().getClass();

System.out.println(c1 == c2);

}

}

结果如下:

true

也就是说我们在实例化ArrayList和实例化ArrayList时是共享一份目标代码的,泛型类类型信息在编译的过程中被擦除了。对于JVM来说,它只看到一份ArrayList(原始类型)而已。我们还可以从反射的角度来理解类型擦除:

public class ErasedType {

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

List list = new ArrayList();

list.add("ABC");

list.getClass().getMethod("add",Object.class).invoke(list,123);

System.out.println(list);

}

}

看结果:

[ABC, 123]

我们很顺利的把Integer型的123插入到了String的List里:)

七 后记

由于类型擦除的存在,我们往往会在使用泛型特性的时候遇到一些诡异的问题,由于篇幅原因,这里不展开了:)我将在另外一篇文章中集中的总结一下这方面的问题。

我的微信号是aristark,欢迎交流指正!

java 泛型 擦除_Java泛型和类型擦除相关推荐

  1. java 类型擦除_java中的类型擦除type erasure

    简介 泛型是java从JDK 5开始引入的新特性,泛型的引入可以让我们在代码编译的时候就强制检查传入的类型,从而提升了程序的健壮度. 泛型可以用在类和接口上,在集合类中非常常见.本文将会讲解泛型导致的 ...

  2. java什么是类型擦除_Java 泛型,你了解类型擦除吗?

    泛型,一个孤独的守门者. 大家可能会有疑问,我为什么叫做泛型是一个守门者.这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇.泛型是 Java 中一个很小巧的概念,但 ...

  3. java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

    原 java泛型(二).泛型的内部原理:类型擦除以及类型擦除带来的问题 2012年08月29日 23:44:10 Kilnn 阅读数:56717 版权声明:本文为博主原创文章,未经博主允许不得转载. ...

  4. 3.1_19 JavaSE入门 P18 【泛型】各类泛型对象、通配符、类型擦除

    相关链接 Excel目录 目录 P18 [泛型]各类泛型对象.通配符.类型擦除 1 什么是泛型 2 泛型类.接口库 2.1 泛型类定义语法 2.2 常用泛型标识 2.3 使用语法 2.4 泛型类注意事 ...

  5. java泛型方法 通配符_Java泛型教程–示例类,接口,方法,通配符等

    java泛型方法 通配符 泛型是Java编程的核心功能之一,它是Java 5中引入的.如果您使用的是Java Collections ,并且版本5或更高版本,则可以肯定使用了它. 将泛型与集合类一起使 ...

  6. java泛型面试_Java泛型面试问题

    java泛型面试 Java面试中的通用面试问题在相当长的时间内在Java 5周围越来越普遍,许多应用程序都转移到Java 5上,并且几乎所有新的Java开发都发生在Tiger(Java 5的代号)上. ...

  7. java 泛型转换_Java泛型—类型转换

    如下代码编译无法通过: classA{}class B extendsA {}public static void funC(ListlistA) {//... }public static void ...

  8. java泛型优点_java泛型的作用和好处

    转载于:https://blog.csdn.net/u012760183/article/details/52092692 之前参加面试的时候遇到面试官问泛型的作用,只是说了大概自己的理解, 在此转载 ...

  9. 一句话,讲清楚java泛型的本质(非类型擦除)

    ?欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 背景 昨天,在逛论坛时遇到个这么个问题,上代码: public class GenericT ...

最新文章

  1. NLLLoss CrossEntropyLoss Pytorch
  2. wxWidgets:wxStyledTextCtrl类用法
  3. html section 布局,HTML:section标签
  4. 【CF#505B】Mr. Kitayuta's Colorful Graph (并查集或Floyd或BFS)
  5. 计算机的我发展趋势,胡生:简述计算机的发展趋势是什么?
  6. 跟我学SpringMVC目录汇总贴、PDF下载、源码下载
  7. linux vi调至文件尾,linux vi从当前行复制到文件尾的命令?
  8. Python format()函数
  9. ios 合并图片显示
  10. MacBook Air开启CPU虚拟化支持(Windows10)
  11. Braintree-国外支付对接(二)
  12. 又到年底冲刺时,华为小米竞相降价促销
  13. pycharm如何打开历史_如何在pycharm中反转控制台历史顺序以进行复制粘贴?
  14. 怎么从安卓设备转移数据到苹果_如何将数据从安卓手机转移到苹果手机
  15. axure RP文件如何找回_AXURE教程:管理后台页面框架
  16. [JAVA]递归实现客户端与服务端之间的文件与文件夹传输
  17. 看完电视剧“天道“的第一次感想记录
  18. 百度云网盘在线播放视频速度加快的方法
  19. Windows磁盘管理工具Diskpart之二 管理动态磁盘
  20. 解决Cleartext HTTP traffic to 01.minipic.eastday.com not permitted

热门文章

  1. 其实,大部分人都不需要你去培养
  2. 程序员上班都在做什么?
  3. 第六节:教你如何在html中绑定数据
  4. 管家婆打印自定义编辑_打印相关,人手一份!
  5. 苹果网页归档转html,常用JS转换HTML转义符
  6. mysql异常关闭7034,SQL ISNULL()、NVL()、IFNULL() 和 COALESCE() 函数
  7. opengl实现经纹理映射的旋转立方体_《图形编程技术学习》(五十三)环境映射...
  8. 监督学习 | 非线性回归 之多项式回归原理及Sklearn实现
  9. R中6种读入表格数据的方式哪个最快?结果出人意料!
  10. 适用于ps的Raw格式图像插件:AdobeCamera Raw13 Mac中文版