简介

泛型是JDK 5引入的概念,泛型的引入主要是为了保证java中类型的安全性,有点像C++中的模板。

但是Java为了保证向下兼容性,它的泛型全部都是在编译期间实现的。编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码。这种就叫做类型擦除。编译器在编译的过程中执行类型检查来保证类型安全,但是在随后的字节码生成之前将其擦除。

这样就会带来让人困惑的结果。本文将会详细讲解泛型在java中的使用,以避免进入误区。

泛型和协变

有关协变和逆变的详细说明可以参考:

深入理解协变和逆变

这里我再总结一下,协变和逆变只有在类型声明中的类型参数里才有意义,对参数化的方法没有意义,因为该标记影响的是子类继承行为,而方法没有子类。

当然java中没有显示的表示参数类型是协变还是逆变。

协变意思是如果有两个类 A<T> 和 A<C>, 其中C是T的子类,那么我们可以用A<C>来替代A<T>。

逆变就是相反的关系。

Java中数组就是协变的,比如Integer是Number的子类,那么Integer[]也是 Number[]的子类,我们可以在需要 Number[] 的时候传入 Integer[]。

接下来我们考虑泛型的情况,List<Number> 是不是 List<Integer>的父类呢?很遗憾,并不是。

我们得出这样一个结论:泛型不是协变的。

为什么呢?我们举个例子:

List<Integer> integerList = new ArrayList<>();
List<Number> numberList = integerList; // compile error
numberList.add(new Float(1.111));

假如integerList可以赋值给numberList,那么numberList可以添加任意Number类型,比如Float,这样就违背了泛型的初衷,向Integer list中添加了Float。所以上面的操作是不被允许的。

刚刚我们讲到Array是协变的,如果在Array中带入泛型,则会发生编译错误。比如new List<String>[10]是不合法的,但是 new List<?>[10]是可以的。因为在泛型中?表示的是未知类型。


List<?>[] list1 = new List<?>[10]; List<String>[] list2 = new List<String>[10]; //compile error

泛型在使用中会遇到的问题

因为类型擦除的原因,List<String>和List<Integer>在运行是都会被当做成为List。所以我们在使用泛型时候的一些操作会遇到问题。

假如我们有一个泛型的类,类中有一个方法,方法的参数是泛型,我们想在这个方法中对泛型参数进行一个拷贝操作。

public class CustUser<T> {

    public void useT(T param){
T copy = new T(param); // compile error
}
}

上面操作会编译失败,因为我们并不知道T是什么,也不知道T到底有没有相应的构造函数。

直接clone T是没有办法了,如果我们想copy一个Set,set中的类型是未定义的该怎么做呢?

    public void useTSet(Set<?> set){
Set<?> copy1 = new HashSet<?>(set); // compile error
Set<?> copy2 = new HashSet<>(set);
Set<?> copy3 = new HashSet<Object>(set);
}

可以看到?是不能直接用于实例化的。但是我们可以用下面的两种方式代替。

再看看Array的使用:

    public void useArray(){
T[] typeArray1= new T[20]; //compile error
T[] typeArray2=(T[]) new Object[20];
T[] typeArray3 = (T[]) Array.newInstance(String.class, 20);
}

同样的,T是不能直接用于实例化的,但是我们可以用下面两种方式代替。

类型擦除要注意的事项

因为类型擦除的原因,我们在接口实现中,实现同一个接口的两个不同类型是无意义的:

public class someClass implements Comparable<Number>, Comparable<String> { ... } // no

因为在编译过后的字节码看来,两个Comparable是一样的。

同样的,我们使用T来做类型强制转换也是没有意义的:

public <T> T cast(T t, Object o) { return (T) o; }

因为编译器并不知道这个强制转换是对还是错。

总结

本文讨论了泛型在java中使用中可能会存在的问题,希望大家能够喜欢。

本文的例子https://github.com/ddean2009/learn-java-collections

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/java-generics-in-deep/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

深入理解java的泛型的更多相关文章

  1. 深入理解Java之泛型

    原文出处: absfree 1. Why ——引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过 ...

  2. 你是怎么理解java的泛型的?

    解答: 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知 ...

  3. 万字长文深入理解java中的集合-附PDF下载

    目录 1. 前言 2. List 2.1 fail-safe fail-fast知多少 2.1.1 Fail-fast Iterator 2.1.2 Fail-fast 的原理 2.1.3 Fail- ...

  4. 转:理解Java泛型

    JDK 5.0 中增加的泛型类型,是 Java 语言中类型安全的一次重要改进.但是,对于初次使用泛型类型的用户来说,泛型的某些方面看起来可能不容易明白,甚至非常奇怪.在本月的“Java 理论和实践”中 ...

  5. 夯实Java基础系列13:深入理解Java中的泛型

    目录 泛型概述 一个栗子 特性 泛型的使用方式 泛型类 泛型接口 泛型通配符 泛型方法 泛型方法的基本用法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型方法总结 泛型上下边界 泛型常见面试 ...

  6. Java 干货之深入理解Java泛型

    一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类.如果要编写可以应用多中类型的代码,这种刻板的限制对代码得束缚会就会很大. ---<Thinking in Java> ...

  7. 如何深入理解Java泛型

    一.泛型的作用与定义 1.1泛型的作用 使用泛型能写出更加灵活通用的代码泛型的设计主要参照了C++的模板,旨在能让人写出更加通用化,更加灵活的代码.模板/泛型代码,就好像做雕塑时的模板,有了模板,需要 ...

  8. 《深入理解Java虚拟机》类文件结构

    上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...

  9. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...

  10. 如何理解 Java 中的 <T extends Comparable<? super T>>

    Java 中类似 <T extends Comparable<? super T>> 这样的类型参数 (Type Parameter) 在 JDK 中或工具类方法中经常能看到. ...

随机推荐

  1. 【Azure 应用服务】"App Service"如何能判断自身网路没有问题?

    问题描述 当创建了一个App Service服务后,如何能判断服务自身网络链路路没有问题? 如果是用VM,通常用Ping可以判断.但是"网站应用App Service" 的Kudu ...

  2. C++ //栈 stack 容器 先进后出 不允许遍历

    1 //栈 stack 容器 先进后出 不允许遍历 2 3 4 #include<iostream> 5 #include<stack> 6 7 using namespace ...

  3. Zabbix技术分享——zabbix命令详解

    在与IT运维的小伙伴交流过程中不少人问到了zabbix_agentd.zabbix_proxy等命令的具体使用问题,为此这一期内容我们来聊聊Zabbix相关命令,其中包括zabbix_server.z ...

  4. C#版开源免费的Bouncy Castle密码库

    前言 今天大姚给大家分享一款C#版开源.免费的Bouncy Castle密码库:BouncyCastle. 项目介绍 BouncyCastle是一款C#版开源.免费的Bouncy Castle密码库, ...

  5. 定时器之PWM

    void PWM_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RC ...

  6. .npmrc 项目的 默认安装配置

    .npmrc registry=http://192.168.77.105:8081/nexus/content/groups/npm-all/

  7. WPF之命令

    目录 命令系统的基本元素 基本元素之间的关系 小试命令 WPF的命令库 命令参数 命令与Binding的结合 近观命令 ICommand接口与RoutedCommand 自定义Command 定义命令 ...

  8. Pod的优先级(priorityClass)和抢占策略(preemptionPolicy)

    Pod优先级 Pod优先级是一个用于指示Pod相对重要程度的整数值.优先级高的Pod在调度时会优先被考虑,并且在资源不足时,优先级较低的Pod可能会被驱逐以释放资源给优先级较高的Pod.Pod优先级的 ...

  9. kettle教程--通过配置文件同步所需要的列数据

    kettle教程--通过配置文件同步所需要的列数据 欢迎关注博主公众号「Java大师」, 专注于分享Java领域干货文章, 关注回复「kettle」, 免费领取全网最热的kettle实战视频教程 ht ...

  10. Maven项目不同jar包相同类名的引用问题

    本文简单记录下一个小问题 问题描述: 在一个Maven项目中,引用了两个jar包,其中两个jar包中,都含有个相同类(包名也相同),这个时候代码里使用该类,出现引用失败的问题 如下图所示,展开两个ja ...