基本概念和原理

为什么使用泛型:

在没有使用泛型之前,一旦把一个对象“丢进”Java集合中,集合就会忘记对象的类型,把所有的对象当成Object类型处理。当程序从集合中取出对象后,就需要进行强制类型转换,这种强制类型转换不仅是代码臃肿,而且容易引起ClassCastException异常。

标题的基本概念:

所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参(或叫泛型)将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)。

使用泛型的好处:
  1. 更好的安全性
    通过使用泛型 ,开发环境和编译器能确保不会用错类型,为程序多设置一道安全防护网。

  2. 更好的可读性
    使用泛型,可以省去繁琐的强制类型转换,再加上明确的类型信息,代码可读性也会更好。

泛型的使用:

public class Test<T> {T first;T second;public Test(T first, T second) {this.first = first;this.second = second;}public T getFirst() {return first;}public T getSecond() {return second;}
}

Test是一个泛型类,与普通类的区别:

  • 类名后多了一个<T>;
  • first和second的类型都是T;

T表示类型参数,泛型就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入。

泛型的原理:

Java有Java编译器和Java虚拟机,编译器将Java源代码转换为.class文件,虚拟机加载并运行.class文件。对于泛型类,Java编译器会将泛型代码转换为普通的非泛型代码,就像上面的普通Test类代码及其使用代码一样,将类型参数T擦除,替换Object,插入必要的强制类型转换。Java虚拟机实际执行的时候,它是不知道泛型这回事的,只知道普通的类及代码。

泛型擦除:

Java泛型是通过擦除实现额,类定义中的类型参数如T会被替换为Object,在程序运行过程中,不知道泛型的实际类型参数,比如Test<Integer>,运行中只知道Test,而不知道Integer。

深入泛型

//定义接口时指定了一个泛型形参,该形参名为E
public interface Test<E> {//在接口方法里,E可作为类型使用//下面方法可以使用E作为类型参数void add(E x);Iterable<E> iterator();//在接口里,E完全可以作为类型使用E next();
}
public interface Map<K, V> {//在接口里K、V完全可以作为类型使用Set<K> keySet();V put(K key, V value);
}

解释:
允许在定义接口、类时声明泛型形参,泛型形参在整个接口、类体内可当成类型使用,几乎所有可使用普通方法类型的地方都可以使用这种泛型形参。

注:
当创建泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明。 例如,为Test<T>类定义构造器,其构造器名依然是Test,而不是Test<T>;!调用该构造器时却可以使用Test<T>的形式,当然应该为T形参传入实际的类型参数。Java7提供了“菱形”语法,允许省略<>中的类型实参。

从泛型类派生子类:

方法中的形参代表变量、常量、表达式等数据,本文把它们直接称为形参,或者称为数据形参。定义方式时可以声明数据形参,调用方法(使用方法)时必须为这些数据形参传入实际的数据;于此类似的是,定义类、接口、方法时可以使用声明泛型形参,使用类、接口、方法时应该为泛型形参传入实际的类型。

//定义类A继承Apple类,Apple类不能跟泛型形参
public class A extends Apple<T> {}   //错误//使用Apple类时为T形参传入String类型
public class A extends Apple<String> //正确

调用方法时必须为所有的数据形参传入参数值, 与调用方法不同的是,使用类、接口时也可以不为泛型形参传入实际的类型参数,即下面代码也是正确的。

public class A extends Apple  //正确

像这种使用Apple类时省略泛型的形式被称为原始类型(raw type)。
如果使用Apple类时没有传入实际的类型(即使用原始类型),Java编译器可能发出警告:使用了未经检查或不安全的操作 - - 就是泛型检查的警告。

并不存在泛型类:

看如下代码:

List<String> list = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
//调用getClass()方法来比较list和list2的类是否相等
System.out.println(list.getClass() == list2.getClass());

运行上面的代码片段,可能有读者认为应该输出false,但实际输出true。因为不管泛型的实际类型参数是什么,它们在运行时总有同样的类(Class)。
不管为泛型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一个类来处理,在内存中也只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量(它们都是类相关的)的声明和初始化中不允许使用泛型形参。

public class Test<T> {//下面代码错误,不能在静态变量声明中使用泛型形参static T info;//正确T age;public void foo(T msg) { }//下面代码错误,不能再静态方法声明中使用使用泛型形参public static void bar(T msg) {}
}

由于系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类。

java.util.Collection<String> cs = new java.util.ArrayList<String>();
//下面代码编译时引起错误:instanceof运算符后不能使用泛型
if(cs instanceof java.util.ArrayList<String>) {}
使用类型通配符:

为了表示各种泛型List的父类,可以使用类型通配符,类型通配符是一个问号(?),将一个问号作为类型实参传给List集合,写作:List<?>(意思是元素类型未知的List)。这个问号(?)被称为通配符,它的元素类型可以匹配任何类型。
看如下代码:

public void test(List<?> c) { }
设定类型通配符的上限(协变):

指定通配符上限的集合,只能从集合中取元素(取出的元素总是上限的类型或其子类),不能向集合中添加元素(因为编译器没法确定集合元素实际是哪种子类型)。

设定类型通配符的下限(逆变):

除可以指定通配符的上限之外,Java也允许指定通配符的下限,通配符的下限用<? super 类型>的方式类指定,通配符下限的作用与通配符上限的作用恰好相反。
Foo是Bar的子类,当程序需要一个A<? super Foo>变量时,程序可以将A<Bar>、A<Object>赋值给A<? super Foo>类型的变量,这种方式称为逆变。
对于逆变的泛型来说,编译器只知道集合元素是下限的父类型,但具体是哪种父类型则不确定。因此,这种逆变的泛型集合能向其中添加元素(因为实际赋值的集合元素总是逆变声明的父类),从集合中取元素时只能被当成Object类型处理(编译器无法确定取出的到底是哪个父类的对象)。

Java:泛型(深入解析,一文读懂)相关推荐

  1. java 审批流_一文读懂工作流

    网上关于工作流引擎有比较多的简介,也有很多工作流的实际应用场景.本文结合笔者多年对工作流的经验来阐述一下对工作流的理解. 一.什么是工作流? 先贴上wiki百科对于工作流的定义 工作流(Workflo ...

  2. java元编程_一文读懂元编程

    元编程(Metaprogramming)是编写.操纵程序的程序,简而言之即为用代码生成代码.元编程是一种编程范式,在传统的编程范式中,程序运行是动态的,但程序本身是静态的.在元编程中,两者都是动态的[ ...

  3. java arraylist排序_一文读懂Java集合框架

    欢迎关注微信公众号:深入浅出Java源码 概念 Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们.集合框架被设计成要满足以下几个目标. 该框架必须是高性能的.基本集合(动态数组,链表, ...

  4. java 委派关系_一文读懂java类加载之双亲委派机制

    一个编译后的class文件,想要在JVM中运行,就需要先加载到JVM中.java中将类的加载工具抽象为类加载器,而通过加载工具加载类文件的具体方式被称为双亲委派机制. 知识点 类加载器:通过一个类全限 ...

  5. 解析|一文读懂AGV的关键技术——激光SLAM与视觉SLAM的区别

    来源:新机器视觉 移动机器人(AGV)是工业机器人的一种.它由计算机控制,具有移动.自动导航.多传感器控制.网络交互等功能,在实际生产中最主要的用途是搬运,可以说只要有搬运需求的地方,就有移动机器人的 ...

  6. Java必备基础一——一文读懂的Java基础中的基础

    内容虽然很简单,但是由于自己的基础很差,练习又太少,在编程这件事情上总是十分吃力,故记于此,长于心. 写在最后,打个鸡血,共勉:真正能让你走远的,都是自律.积极和勤奋. 程序人生,你可以靠努力去实现的 ...

  7. 一文读懂HttpServletRequest

    点击上方"好好学java",选择"置顶公众号" 优秀学习资源.干货第一时间送达! 精彩内容 java实战练习项目教程 全网最全电子图书分享 你所需要的大数据视频 ...

  8. JVM(一)一文读懂Java编译全过程

    一文读懂Java编译全过程 java代码首先要通过前端编译器编译成.class字节码文件,然后再按一定的规则加载到JVM(java 虚拟机)内运行,有三种运行方式,解释模式(javac).编译模式(C ...

  9. 一文读懂Java中File类、字节流、字符流、转换流

    一文读懂Java中File类.字节流.字符流.转换流 第一章 递归:File类: 1.1:概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. ...

  10. java中date类型如何赋值_一文读懂java中的Reference和引用类型

    简介 java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型.java为引用类型专门定义了一个类叫做Reference.Reference是跟jav ...

最新文章

  1. Heartbeat双机热备配置
  2. MySQL必知必会读书笔记
  3. 用Uber的方式管理机器学习模型
  4. Redis3.0.5配置文件详解
  5. arm ida 伪代码 安卓 符号表_IDA调试界面介绍及快捷键
  6. python快速入门答案-Python 开发 14 天快速入门
  7. mysql数据清洗_mysql数据库如何实现亿级数据快速清理
  8. 详解Node.js包的工程目录与NPM包管理器的使用_node.js
  9. C++学习笔记:(四)运算符重载 类型转换
  10. 晋升,结婚,出书,买房,这就是我的2019年!
  11. 分享12306抢票心得-最终篇
  12. Windows下的修改Tomcat的可用内存
  13. Python 完整学习路线,非常赞!
  14. 高等数学-用等价无穷小求极限题集
  15. Acer 4750 安装黑苹果_黑苹果 MacOS 10.15 Catalina安装教程
  16. csdn博客改用户名,取名字慎重
  17. 服务器流量异常的原因分析和解决办法
  18. 如何将CSDN博客下载为PDF文件
  19. 网景创始人:软件应用无所不在 正吞噬整个世界
  20. 读书笔记002:《伤寒论》-十二经络

热门文章

  1. 学习嵌入式要学哪些课程?
  2. QCPColorMap实现瀑布图
  3. 安卓持久框架Room VS GreenDao
  4. 栈——后入先出的数据结构(LIFO)
  5. 运维排查篇 | 访问nginx出现403错误
  6. 设计原则(安卓图片类)
  7. 我和后端因为接口吵起来啦,还列了 5 锅罪
  8. OSChina 周五乱弹 —— 源花?真土鳖,我要开源小仙女!
  9. 酷派把用户手机当肉鸡,非一天两天
  10. HDU 3179 二叉搜索树(树的建立)