背景

周五本该是愉快的,可是今天花了一个早上查问题,为什么要花一个早上?我把原因总结为两点:

  • 日志信息严重丢失,茫茫代码毫无头绪。
  • 对泛型的认识不够,导致代码出现了BUG。

第一个原因可以通过以后编码谨慎的打日志来解决,我们今天主要来一起回顾下JAVA泛型基础。

一个小栗子

先看下面一个例子,test1实例化一个List容器的时候没有指定泛型参数,那么我们可以往这个容器里面放入任何类型的对象,这样是不是很爽?但是当我们从容器中取出容器中的对象的时候我们必须小心翼翼,因为容器中的对象具有运行时的类型信息,这意味着你不能够将一个带有运行时类型信息的对象赋值给另一个类型,否则ClassCastException

@Test
public void test1() throws Exception {
List list = new ArrayList();
list.add("float.lu");
list.add(1); String name = (String) list.get(0);
int num = (Integer) list.get(1);
System.out.println(String.format("name[%s], num[%s]", name, num));
}

上面的代码没问题,可以很好地编译和运行通过,问题是我必须要事先很清楚地知道容器中的索引为0的对象是什么类型,索引为1的对象是什么类型,很显然,这在实际应用中是不切实际的,也是一种很不靠谱的做法,那么这个问题如何解决呢?泛型。

引入泛型

为了解决这个问题,我们引入泛型,下面代码可以看出与上面不同的是我们在实例化容器的时候加了<String>这个东西,这个东西的学名叫做泛型参数,就像普通方法带有参数一样,interface List<E>中的E为形式参数、而String为实参。

@Test
public void test2() throws Exception {
List<String> list = new ArrayList<String>();
list.add("a");
list.add(1)//1
}

引入泛型后,我们规定这个容器中只能存放类型为字符串类型的对象,好的,编译器可以识别泛型并帮我们检查编译错误,上面的代码中1处会出现编译错误。注意:泛型信息仅仅存在于编译期间,编译器可以通过泛型信息来对代码是否存在违规行为(编译错误)来进行检查,当编译器将代码编译为字节码之后,泛型信息将不复存在,然而对象的运行时信息仍然是有的,这就解释了为什么会出现ClassCastException。

别高兴太早

有了泛型我们可以让代码安全地通过编译,并且我们认为他是安全的了,嘿嘿,是否就真的安全了呢?是否就能和ClassCastException说拜拜了呢?答案是:NO。看看下面这段代码:

@Test
public void test3() throws Exception {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b"); List _list = list;
List<Integer> integerList = _list;
for (Integer item : integerList) {
System.out.println(String.format("item[%s]", item));
}
}

上面这段代码编译没有问题,我们没有直接将泛型参数为String的容器赋值给泛型参数为Integer的容器,而是花了点点小心思,我们现将list赋值给_list,_list生命为可以存储任何类型,也就相当于无特定类型,而后我们又把_list赋值给integerList容器,integerList容器被声明为只能存储类型为Integer的对象。悲催的是这段代码在运行的时候报了ClassCastException,很明显,我们知道在迭代integerList容器中的对象的时候,这些对象是有运行时类型信息的,当带有String类型信息的对象赋值给Integer的时候显然就报错了。这一切看起来似乎没问题,符合逻辑,但是有一个问题我们还没有问:为什么会没有编译错误?

泛型术语

在学习数学的时候我们往往会对一个证明题进行论证,而论证之前我们手上往往会有一些不需要证明的已知定理,下面这些“定理”将被用来直接回答上一节中遗留的问题。

  • List<E>被称作泛型类型。
  • List<E>中的E被称为类型变量或类型参数。
  • List<String>被称为参数化类型。
  • List<String>中的String被称为实际类型参数。
  • List<E>中的<>年typeof。
  • List被称为原始类型。
  • 参数化类型可以引用一个原始类型对象,编译报告警告。
  • 原始类型可以引用一个参数化类型对象,编译报告警告。

由上可知,List<Integer> integerList = _list;可以通过编译。

看清本质

经过上面的一些小波折,我们了解一些关于泛型的本质:泛型是给javac编译器使用的,javac是JAVA的编译器,而泛型可以让代码在编译期间确定类型安全,比如我们告诉编译器某个容器只能存储某种类型的对象,那么编译器会为我们好好地检查,确保类型安全,但是安全是相对的,只要我们逃过编译器,我们就有一百种方法让代码ClassCastException(比如反射)。同时编译之后参数化类型在运行时没有任何泛型信息,也就是为什么List.class和List<String>.class是同一个东西。除了参数化类型之外,容器中的对象在运行的时候是有类型信息的,也就是为什么会ClassCastExcetion。关于泛型还有很多内容,这里不做多讲,文中有误也欢迎留言讨论。

一个小栗子聊聊JAVA泛型基础的更多相关文章

  1. Java:泛型基础

    泛型 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚很多! 解决这种限制的 ...

  2. java泛型基础

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法.  Ja ...

  3. 遇到个小问题,Java泛型真的是鸡肋吗?

    今天遇到一个小问题,让我感觉Java的泛型(因为背负了历史的包袱导致的)有点鸡肋啊. 我们经常会遇到要一些自定义的key-value字符串,比如: "key1:1k;key2:2;key3: ...

  4. java 泛型基础问题汇总

    泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java语言引 ...

  5. 【Puppeteer】puppeteer安装/常用的方法以及一个小栗子(Youtube油管自动评论)

    这里介绍的是Win平台的安装方法,其他平台请至Github>Puppeteer. 首先要安装node.js 可以看我这篇的开头>[Angular]学习笔记-环境部署.项目建立相关 1.新建 ...

  6. 黑马----JAVA泛型基础

    黑马程序员:Java培训.Android培训.iOS培训..Net培训 JAVA范型-基础 一.泛型的概念 1.实现了参数化类型 2.用于编写可应用于多种类型的代码,即所编写的代码可应用于许多许多的类 ...

  7. java泛型基础、子类泛型不能转换成父类泛型

    参考http://how2j.cn/k/generic/generic-generic/373.html 1.使用泛型的好处:泛型的用法是在容器后面添加<Type>Type可以是类,抽象类 ...

  8. java泛型基础、子类泛型不能转换成父类泛型--未完待续

    参考http://how2j.cn/k/generic/generic-generic/373.html 1.使用泛型的好处:泛型的用法是在容器后面添加<Type>Type可以是类,抽象类 ...

  9. 201671010127 2016—2017-2 通过一个小程序对Java的再认识。

    学习了将近四周的Java语言,对于Java语言,我也有了更进一步的理解,出于对Java语言的喜爱,我总是喜欢没事的时候,自己敲一些很简单的代码,一边学习Java语言,一边对比C语言,往往可以帮助我们更 ...

随机推荐

  1. windows和linux 下将tomcat注册为服务

    参考文献: tomcat注册成windows服务 背景 当前项目需要运行两个Tomcat,每次启动系统以后都要手动进入到tomcat目录执行startup.bat,非常烦,所以想将这两个tomcat直 ...

  2. Go 语言中的 new() 和 make()的区别

    本文是看了文章之后的心得. 在此感谢. 概述 Go 语言中的 new 和 make 一直是新手比较容易混淆的东西,咋一看很相似.不过解释两者之间的不同也非常容易. 他们所做的事情,和应用的类型也不相同 ...

  3. 读写文件:每次读入大文件里的一行、读写.CSV文件

    读文件: 传统的读法.所有读出,按行处理: fp=open("./ps.txt", "r"); alllines=fp.readlines(); fp.clos ...

  4. 体验h5离线缓存

    摘要 Application Cache是浏览器自己的一种机制,随着移动互联网时代的到来,如果我们已经将需要的文件缓存下下来,一旦网络无法访问,也能继续访问.不仅能提高用户体验,而且在有网络时,也能直 ...

  5. 十步轻松搞定IIS+PHP环境搭建

    突然心血来潮想着自己一直使用Apache+php的模式,想要了解一下IIS+php的模式.说起来也算是九曲十八弯吧! 第一部分:以ISAPI.dll 扩展的形式 结果按照资料上面说的我就是找不到一个i ...

  6. SQLCE使用

    Windows Phone的本地数据库SQL Server CE是7.1版本即芒果更新的新特性,所以你要在应用程序中使用SQL Server CE数据库必须使用Windows Phone 7.1的AP ...

  7. Java 下载文件

    public @ResponseBody void exportExcel(HttpServletRequest request, HttpServletResponse response, Khxx ...

  8. websocket消息推送实现

    一.服务层 package com.demo.websocket; import java.io.IOException; import java.util.Iterator; import java ...

  9. 基于Memcached的tomcat集群session共享所用的jar

    多个tomcat各种序列化策略配置如下:一.java默认序列化tomcat配置conf/context.xml添加<Manager className="de.javakaffee.w ...

  10. 盾牌第一至七季/全集The Shield迅雷下载

    英文译名The Shield,第1-7季(2002-2008)FX.本季看点:<盾牌>一部极具争议性的连续剧,打破了传统警匪片套路,刻画了性格复杂的警察,他们在与各种罪案做斗争的同时,也面 ...