对于范型的使用或者说印象只有集合,其他地方即使使用过也不知道,反正就是只停留在List<E> Map<K, V>,最近刚好闲来无事,就找找资料学习一下;下列为个人学习总结,欢迎学习交流;

1. 什么是java泛型

范型:参数化类型,所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法;

List<Integer> list = new ArrayList<>();

上述代码申明了一个集合,操作的数据类型被指定为Integer(此处Integer为类型参数);

2. 为什么需要泛型

引入泛型的好处是可以将运行时错误提前到编译时错误

List list = new ArrayList();
list.add(100);
list.add("zhangsan"); for(int i = 0; i< list.size();i++){
int num = (int)list.get(i);
}

上面的代码在编译时没有任何问题,但是在运行时会报错:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

如果没有引入范型,集合内的操作数据类型可以是任何类型,如果在操作数据时进行类型判断然后在强转也是没有问题的,但是很明显不切合实际,所以如果引入范型对操作数据类型做一定的约束的话,将会对后续的操作提供太多的方便也能减少错误的出现;

List<Integer> list = new arrayList<>();
//list.add("zhangsan");//在编译期就会报错

3. 范型的使用

泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法

3.1. 范型类

泛型类中的类型参数几乎可以用于任何可以使用接口名、类名的地方

/**
* 范型标识可以是任何标识符号,如常见的E, T, K, V ...
*/
class 类名<范型标识>{
} class Stu<T>{
} class People<E>{
}

注意:

  • 泛型的类型参数只能是类类型,不能是基础类型;
List<int> list;//基础类型不能当作类型参数
  • 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。
if(item instanceof List<Integer>) // Illegal generic type of instanceof

3.2. 范型接口

泛型接口与泛型类的定义及使用基本相同

public interface Iterable<T> {
Iterator<T> iterator();
}
  1. 实现范型接口,未明确范型时:实现类后的范型标识不能省略
class Iter<T> implements Iterable<T> {
@Override
public Iterator<T> iterator() {
return null;
}
}
  1. 实现范型接口,明确范型时:实现类后的范型标识省略
class Iter1 implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return null;
}
}

3.3. 范型方法

public 与 返回值 中间非常重要,声明此方法为泛型方法;

/**
* 泛型方法的基本介绍
* @param tClass 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1)public 与 返回值 中间<T>非常重要,声明此方法为泛型方法;
* 2)只有声明了<T>的方法才是泛型方法,若没有<T>泛型类中即使使用了泛型的成员方法也不是泛型方法;
* 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T;
* 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型;
*/
public <T> T genInstance(Class<T> tClass) throws IllegalAccessException, InstantiationException {
T instance = tClass.newInstance();
return instance;
}

光看上面的例子可能依然会非常迷糊,我们再通过一个例子

public class ArrayList<E> implements List<E> {

    /**
* 虽然在方法中使用了泛型,但是这并不是一个泛型方法。
* 这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
* 所以在这个方法中才可以继续使用 T 这个泛型。
*/
public E get(int index) {
//...
return elementData(index);
} /**
* 将E改为T后,方法报错,"cannot reslove symbol T"
* 因为在类的声明中并未声明泛型T,所以在使用E做形参和返回值类型时,编译器会无法识别
*/
public T set(int index, T element) {
//...
return oldValue;
} /**
* 这才是一个真正的泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
*/
public <T> T[] toArray(T[] a) {
//...
return a;
}
}

4. 范型通配符

使用?代替范型标识,标识操作数据类型可以为任何数据类型

java中我们都知道父类可以出现的地方,子类都是可以出现的,但是:

public static void method(List<Number> list){
} public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
method(list); // 此处报错:List<java.lang.Number> cannot be applied to List<java.lang.Integer>
}

通过提示信息我们可以看到List<Integer>不能被看作为List<Number>的子类。

由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。

我们可以将上面的方法改一下:

public static void method(List<?> list){
}

类型通配符一般是使用?代替具体的类型实参,此处的和Number、String、Integer一样都是一种实际的类型,可以把看成所有类型的父类,是一种真实的类型;

可以解决当具体类型不确定的时候,这个通配符就是?;当不需要使用类型的具体功能只使用Object类中的功能,那么可以用 ? 通配符来表未知类型。

5. 泛型上下边界

在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。

public static void method(List<? extends Number> list){
} public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
method(intList); // Integer 是Number的子类 List<Double> doubleList = new ArrayList<>();
method(doubleList); // Double是Number的子类 List<String> strList = new ArrayList<>();
method(strList); // 此处报错,String不是Number的子类
}

6. 范型擦除

Java在编译期间,所有的泛型信息都会被擦掉;

如在代码中定义List<Object>List<String>等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法预知在运行时刻出现的类型转换异常的情况;

擦除范型后只保留原始类型

原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

List<String> list1 = new ArrayList<>();
list1.add("abc"); List<Integer> list2 = new ArrayList<>();
list2.add(123); System.out.println(list1.getClass() == list2.getClass()); // true
System.out.println(list1.getClass()); // class java.util.ArrayList

说明泛型类型String和Integer都被擦除掉了,只剩下原始类型List;

ArrayList<Integer> list = new ArrayList<Integer>();

list.add(1);  //这样调用 add 方法只能存储整形,因为泛型类型的实例为 Integer
list.add("asd"); // 此处报错 list.getClass().getMethod("add", Object.class).invoke(list, "asd"); // 通过反射获取实例后可以添加成功 for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}

在程序中定义了一个ArrayList泛型类型实例化为Integer对象,如果直接调用add()方法,那么只能存储整数数据,不过当我们利用反射调用add()方法的时候,却可以存储字符串,这说明了Integer泛型实例在编译之后被擦除掉了,只保留了原始类型。

参考资料:

https://www.cnblogs.com/wuqinglong/p/9456193.html

https://blog.csdn.net/caihuangshi/article/details/51278793

https://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html

https://blog.csdn.net/caihuangshi/article/details/51278793

Java范型学习笔记的更多相关文章

  1. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  2. java之jvm学习笔记十三(jvm基本结构)

    java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成 ...

  3. Java:NIO 学习笔记-2

    Java:NIO 学习笔记-2 上一篇 NIO 学习笔记-1 看了 尚硅谷 的相应教程,此处又对比看了 黑马程序员 的课程 JAVA通信架构I/O模式,做了相应的笔记 前言 在 Java 的软件设计开 ...

  4. Java:NIO 学习笔记-1

    Java:NIO 学习笔记-1 说明:本笔记是根据bilibili上 尚硅谷 的课程 NIO视频 而做的笔记 主要内容 Java NIO 简介 Java NIO 与 IO 的主要区别 缓冲区(Buff ...

  5. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  6. Java范型随笔

    最近在帝都好无聊啊, 排遣寂寞就只有让自己不要停下来,不断的思考了 QWQ; 最近做ndk, java有点忘了,突然看到了一些java范型方面的问题, 踌躇了一会, 想着想着,决定还是写个随笔记录下来 ...

  7. Java多线程技术学习笔记(二)

    目录: 线程间的通信示例 等待唤醒机制 等待唤醒机制的优化 线程间通信经典问题:多生产者多消费者问题 多生产多消费问题的解决 JDK1.5之后的新加锁方式 多生产多消费问题的新解决办法 sleep和w ...

  8. Java安全防御学习笔记V1.0

    Java安全防御学习笔记V1.0http://www.docin.com/p-766808938.html

  9. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

随机推荐

  1. Django之form组件自动校验数据

    目录 一.form介绍 二.普通方式手写注册功能 views.py register.html 三.使用form组件实现注册功能 views.py register2.html 四.pycharm的专 ...

  2. Some collections were archived because you’ve reached the shared requests limits.错误解决

    今天打开我的postman 发现我的一个collection不见了,左下角出现一个提示, Some collections were archived because you’ve reached t ...

  3. 小白学 Python 爬虫(41):爬虫框架 Scrapy 入门基础(八)对接 Splash 实战

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  4. vue的param和query两种传参方式及URL的显示

    路由配置: // 首页 { path: '/home', name:'home', component:Home }, // 行情 { path: '/markets', name:'market', ...

  5. 大白话原型模式(Prototype Pattern)

    意图 原型模式是创建型设计模式,可以复制已存在的对象而无需依赖它的类. 问题 假如现在有一个对象,我们想完全复制一份新的,我们该如何做? 创建同一个类的新对象 遍历所有已存在对象的值,然后将他们的值复 ...

  6. 两个大数相乘 - 高精度FFT

    HDU 1402 A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  7. MST + 树形 dp

    Genghis Khan(成吉思汗)(1162-1227), also known by his birth name Temujin(铁木真) and temple name Taizu(元太祖), ...

  8. http GET 和 POST 请求的优缺点和误区 --前端优化

    Get和Post在面试中一般都会问到,一般的区别:(1)post更安全(不会作为url的一部分,不会被缓存.保存在服务器日志.以及浏览器浏览记录中)(2)post发送的数据更大(get有url长度限制 ...

  9. arima.predict()参数选择以及相关的一些问题

    在使用a ri ma进行模型建立时,需要注意以下几点 1.参数选择上predict必须起始时间在原始的数据及当中的,在下例中就是说2017必须在数据集里面,而2019不受限制,只哟在2017后面就好了 ...

  10. ORM跨表查询总结

    一.基于对象的查询-->子查询 1.一对多 正向查询:基于 字段 反向查询:基于 表名__set.all() 注意:表名全部小写 2.多对多 正向查询:基于 字段.all() 反向查询:基于 表 ...