一、泛型的意义

泛型,又叫 显式参数多态,在c++里又叫模板。在强类型的编程语言中普遍作用是:

1. 可以显式地使用参数注入,实现代码复用(避免粗暴地使用 Object)

2. 加强编译时的类型安全(类型检查)

3. 减少类型转换的次数

二、Java 中的 泛型

编译时进行类型擦除生成与泛型类同名的原始类,但类型参数都被删去了。类型变量由类型限界代替(通常为 Object)。

三、集合协变与泛型上下界

Java 中泛型集合不具有协变性,无法利用多态替换参数。需要使用通配符,用于接收某些类型。 (PS: Java 中矩阵或者数组具有协变性,因此数组参数具有多态性。)

public static double totalArea(Collection<? extends Shape> arr)

四、泛型擦除

问题1: 什么时候需要避免泛型擦除?

使用泛型,程序在编译时,类型会完全擦除或保留部分(如果定义了上下限)。

涉及到具体类型的操作时会自动加上强制转换。比如:

public static void main(String[] args) {
List<Integer> m = new ArrayList<>();
m.add(3);
System.out.println(m.get(0) + 5);
}

编辑器会替换为:

public static void main(String[] args) {
List m = new ArrayList();
m.add(3);
System.out.println((Integer)m.get(0) + 5);
}

类型擦除还会造成以下编译时错误:

错误 ① “Erasure of method xyz(…) is the same as another method in type Abc”

public class App {
public int process(List<Person> people) {
for (Person person : people) {
log.info("Processing person: " + person.toString());
}
return person.size();
} public int process(List<Employee> employees) {
for (Employee employee : employees) {
log.info("Processing employee: " + employee.toString());
}
return employees.size();
}
}

以上代码由于类型擦除,会被编译成:

public class App {
public int process(List people) {
for (Person person : people) {
log.info("Processing person: " + person.toString());
}
return person.size();
}
public int process(List employees) {
for (Employee employee : employees) {
log.info("Processing employee: " + employee.toString());
}
return employees.size();
}
}

那么存在的错误显而易见。两个具有相同签名 ( process(List) ) 的方法无法在同一个类中共存,简单的解决方法是修改某一个方法的签名。

错误 ② “The method xyz(Foo) in the type Abc is not applicable for the arguments (Foo)”

public class App {
public int processPeople(List<Person> people){
for (Person person : people) {
log.info("Processing person: " + person.toString());
}
return person.size();
}
..
}
..
List<Employee>employees;
employees = new ArrayList<>();
employees.add(employee1);
employees.add(employee2);
App app = new App();
// ERROR ON NEXT LINE!
app.processPeople(employees);..

这个错误的原因是泛型集合不具有协变性。无法强制转换。

类型擦除有时还会影响序列化性能(因为不知道类型信息,从而无法针对性地选择序列化方式,因此影响了序列化的性能)。

问题2: 如何避免泛型擦除?

第一,使用泛型边界。可以有限地保留类型信息。如:

LinkedList<? extends Building>,LInkedList<? super House>

分别设置了上界和下界。在这种情况下编译器不会将泛型信息完全擦除,比如上述两个List经过编译后分别编程了LinkedList<Building> 和 LinkedList<House>。

第二,借助类型推断隐式确定具体参数类型,常用于函数式编程类型推断(需要编程语言支持)。

第三,不直接使用泛型。比如定义一个新的类型,继承或实现原泛型类或接口,并且注入具体的参数类型,则不会发生类型擦除。如:

env.fromElements(1, 2, 3)
.map(i -> new DoubleTuple(i, i))
.print(); public static class DoubleTuple extends Tuple2<Integer, Integer> {
public DoubleTuple(int f0, int f1) {
this.f0 = f0;
this.f1 = f1;
}
}

参考


https://www.ibm.com/developerworks/cn/java/java-language-type-erasure/index.html

Java 中的泛型的更多相关文章

  1. Java中的泛型 (上) - 基本概念和原理

    本节我们主要来介绍泛型的基本概念和原理 后续章节我们会介绍各种容器类,容器类可以说是日常程序开发中天天用到的,没有容器类,难以想象能开发什么真正有用的程序.而容器类是基于泛型的,不理解泛型,我们就难以 ...

  2. [JavaCore]JAVA中的泛型

    JAVA中的泛型 [更新总结] 泛型就是定义在类里面的一个类型,这个类型在编写类的时候是不确定的,而在初始化对象时,必须确定该类型:这个类型可以在一个在里定义多个:在一旦使用某种类型,在类方法中,那么 ...

  3. Java 中的泛型详解-Java编程思想

    Java中的泛型参考了C++的模板,Java的界限是Java泛型的局限. 2.简单泛型 促成泛型出现最引人注目的一个原因就是为了创造容器类. 首先看一个只能持有单个对象的类,这个类可以明确指定其持有的 ...

  4. 【Java入门提高篇】Day14 Java中的泛型初探

    泛型是一个很有意思也很重要的概念,本篇将简单介绍Java中的泛型特性,主要从以下角度讲解: 1.什么是泛型. 2.如何使用泛型. 3.泛型的好处. 1.什么是泛型? 泛型,字面意思便是参数化类型,平时 ...

  5. Java开发知识之Java中的泛型

    Java开发知识之Java中的泛型 一丶简介什么是泛型. 泛型就是指泛指任何数据类型. 就是把数据类型用泛型替代了. 这样是可以的. 二丶Java中的泛型 Java中,所有类的父类都是Object类. ...

  6. Java中的泛型 --- Java 编程思想

    前言 ​ 我一直都认为泛型是程序语言设计中一个非常基础,重要的概念,Java 中的泛型到底是怎么样的,为什么会有泛型,泛型怎么发展出来的.通透理解泛型是学好基础里面中非常重要的.于是,我对<Ja ...

  7. 第九节:详细讲解Java中的泛型,多线程,网络编程

    前言 大家好,给大家带来详细讲解Java中的泛型,多线程,网络编程的概述,希望你们喜欢 泛型 泛型格式:ArrayList list= new ArrayList(); ArrayList list= ...

  8. Java基础之Java中的泛型

    1.为什么要使用泛型 这里我们俩看一段代码; List list = new ArrayList(); list.add("CSDN_SEU_Cavin"); list.add(1 ...

  9. java中的泛型2--注意的一些问题和面试题

    前言 这里总结一下泛型中需要注意的一些地方和面试题,通过面试题可以让你掌握的更清楚一些. 泛型相关问题 1.泛型类型引用传递问题 在Java中,像下面形式的引用传递是不允许的: ArrayList&l ...

随机推荐

  1. PDF.js实现个性化PDF渲染(文本复制)

    我肥来啦

  2. Eclipse预编译的include路径与宏定义

  3. Swagger Annotation 详解(建议收藏)

    转载:https://www.jianshu.com/p/b0b19368e4a8 在软件开发行业,管理文档是件头疼的事.不是文档难于撰写,而是文档难于维护,因为需求与代码会经常变动,尤其在采用敏捷软 ...

  4. HashSet代码分析

    HashSet (jdk 1.7)的继承关系如下: HashSet是使用HashMap实现的一个没有重复元素的集合.HashSet用法如下: HashSet<String> hashSet ...

  5. C# System.IO.StreamReader

    实现一个 TextReader,使其以一种特定的编码从字节流中读取字符. using System; using System.IO; class Test { public static void ...

  6. Java虚拟机垃圾回收:内存分配与回收策略 方法区垃圾回收 以及 JVM垃圾回收的调优方法

    在<Java对象在Java虚拟机中的创建过程>了解到对象创建的内存分配,在<Java内存区域 JVM运行时数据区>中了解到各数据区有些什么特点.以及相关参数的调整,在<J ...

  7. linux下 彻底修改python的包/模块导入路径

    python模式下,有时候需要导入  import某些模块或者包.明明这个模块/包是存在的,却提示导入错误,比如,“ImportError: No module named lxml”. 但是当你在命 ...

  8. redmine在linux上的mysql性能优化方法与问题排查方案

    iredmine的linux服务器mysql性能优化方法与问题排查方案     问题定位:   客户端工具: 1. 浏览器inspect-tool的network timing工具分析   2. 浏览 ...

  9. CentOS 6.5 x64下查找依赖包,或用YUM安装

    查看某个命令YUM上的安装源 1)当某个命令不存时进行查询所依赖的包,如:pstree [root@localhost ~]# yum provides pstree 已加载插件:fastestmir ...

  10. 修改Dreamweaver CC 2017 代码背景颜色

    Windows系统路径: E:\Program Files\Adobe\Dreamweaver CC\www\extensions\default\LightTheme\main.less (如果用的 ...