泛型

1、泛型的概述

在JDK1.5之前,把对象放入到集合中,集合不会记住元素的类型,取出时,全都变成Object类型。
泛型是jdk5引入的类型机制,就是将类型参数化,它是早在1999年就制定的jsr14的实现。泛型机制将类型转换时的类型检查从运行时提前到了编译时,
使用泛型编写的代码比杂乱的使用object并在需要时再强制类型转换的机制具有更好的可读性和安全性。
例如在集合接口中,集合类中出现的<>就是泛型,即参数化类型 ,<>中的字母代表的是类型。
泛型程序设计意味着程序可以被不同类型的对象重用。

  1. 可读性,从字面上就可以判断集合中的内容类型;
  2. 类型检查,避免插入非法类型。
  3. 获取数据时不在需要强制类型转换

总之:

泛型(Generics)是把类型参数化,运用于类、接口、方法中,可以通过执行泛型类型调用分配一个类型,将用分配的具体类型替换泛型类型。
然后,所分配的类型将用于限制容器内使用的值,这样就无需进行类型转换,还可以在编译时提供更强的类型检查。

2、泛型的使用标准

类型参数(又称类型变量)用作占位符,指示在运行时为类分配类型。

根据需要,可能有一个或多个类型参数,并且可以用于整个类。

根据惯例,类型参数是单个大写字母,该字母用于指示所定义的参数类型。下面列出每个用例的标准类型参数:

3、泛型类

在类上添加一个类型的定义,在使用类时指定一个类型,就可以对传入的数据进行类型检查,

在取出时,不必进行类型转换了,这样的类就是泛型类。

泛型类的缺点:
每次针对不同的类型的参数,都必须创建不同类型的对象,这使得方法依赖于创建对象时的类型,它们之间的耦合度太高了。
为了降低这种耦合度,可以把泛型定义在方法上,这就是泛型方法。

4、泛型方法

泛型类的问题:带泛型参数的方法,和对象的类型耦合度太高.
泛型方法:把泛型直接定义在方法上.调用方法的时候,确定参数的类型

修饰符 <T> 返回值类型 方法名(){

}

严格的调用方式:

对象.<T>method()

一般情况下调用时可以省略:

对象.method()

5、泛型方法和泛型类混用看归属

泛型方法有自己的类型参数,泛型类的成员方法使用的是当前类的类型参数。

方法中有<T> 是泛型方法;没有的,称为泛型类中的成员方法。

package wang;

class Clazz1<E>{
public void test1(E e){
System.out.println(e.getClass());
} public <E> void test2(E e){
System.out.println(e.getClass());
}
public <T> void test3(T e){
System.out.println(e.getClass());
}
public <T> void test4(E e){
System.out.println(e.getClass());
} }
public class GenericDemo4 {
public static void main(String[] args) {
Clazz1<String> clazz1=new Clazz1<>();
clazz1.test1(1); clazz1.test2(1); clazz1.test3(1); clazz1.test4(1);
} }

泛型方法和泛型类归属

6、泛型的限定

6.1泛型的限定:

如果限制只有特定某些类可以传入T参数,那么可以对T进行限定,如:只有实现了特定接口的类:<T extends Human>,表示的是Human及其子类型。

为什么是extends不是 implements,或者其他限定符?

该表达式意味着:`T subtypeOf Human`,jdk不希望再引入一个新的关键词;

其次,T既可以是类对象也可以是接口,如果是类对象应该是`Human`,而如果是接口,则应该是`extends`;从子类型上来讲,extends更接近要表达的意思。

限定符可以指定多个类型参数,分隔符是 &,不是逗号,因为在类型参数定义中,逗号已经作为多个类型参数的分隔符了,如:<T,S extends Comparable & Serializable>。

泛型限定的优点:

限制某些类型的子类型可以传入,在一定程度上保证类型安全;

可以使用限定类型的方法。

加上限定符,就可以访问限定类型的方法了,类型更明确。

注:

我们知道final类不可继承,在继承机制上class SomeString extends String是错误的,但泛型限定符使用时是可以的:<T extends String>,只是会给一个警告。

后面的通配符限定有一个super关键字,这里没有。

6.2通配符:

子类型限定通配符,又称上边界通配符(upper bound wildcard Generics),代表继承它的所有子类型,通配符匹配的类型不允许作为参数传入,只能作为返回值。

下边界通配符(lower bound wildcard Generics),通配符匹配的类型可以为方法提供参数,能得到返回值。

List<? extends T> 大家以为元素为 T以及其所有子类的对象 的List。其实不是。元素类型 仅指T的某一个不确定的子类,是单一的一个不确定类,没有具体哪个类。因此不能插入一个不确定的。

List<? super T> 大家以为元素为 T以及其父类的对象 的List。其实不是,元素类型 仅指T的某一个不确定的父类,是单一的一个不确定类(只确定是T的父类),没有具体哪个类。

import java.util.ArrayList;

public class GTtest {
static class Species{};
static public class Human extends Species{};
static public class Man extends Human{};
static public class Woman extends Human{};
public static void main(String[] args) {
ArrayList<Human> list = new ArrayList<Human>();
list.add(new Man());
list.add(new Woman());
Man human1 = (Man)list.get(0);//OK
System.out.println(human1);
Man human2 = (Man)list.get(1);
System.out.println(human2);//运行时报错 ArrayList<? extends Human> list2= new ArrayList<>();
list2.add(new Man());//编译时报错
list2.add(new Woman());//编译时报错
list2.add(new Human());//编译时报错
list2.add(new Species());//编译时报错 ArrayList<? super Human> list3= new ArrayList<>();
list3.add(new Man());//OK
list3.add(new Woman());//OK
list3.add(new Human());////OK
list3.add(new Species());//编译时报错 } }

泛型的限定

因此:

不能往List<? extends T>中插入任何类型的对象。唯一可以保证的是,你可以从中读取到T或者T的子类。

可以往List<? super T>中插入T或者T子类的对象,但不可以插入T父类的对象。可以读取到Object或者Object子类的对象(你并不知道具体的子类是什么)。

总结:

  • 如果频繁支持读取数据,不要求写数据,使用<? extends T>。即生产者 使用 <? extends T>

  • 如果频繁支持写入数据,不特别要求读数据,使用<? super T>。即消费者 使用 <? super T>

  • 如果都需要支持,使用<T>。

无限定通配符
Pair<?> 就是 Pair<? extends Object>

因此,无限定通配符可以作为返回值,不可做入参。

返回值只能保存在Object中。

P<?> 和P

Pair可以调用setter方法,这是它和Pair<?>最重要的区别。

P<?> 不等于 P<Object>

P<Object>是P<?>的子类。

通配符的捕获

通配符限定类中可以使用T,编译器适配类型。

使用通配类型创建一个swap方法交换key-value,交换时需要先使用一个临时变量保存一个字段:

这里有一个办法解决它,再封装一个swapHelper():

这种方式,称为:通配符捕获,用一个Pair<T> 来捕获 Pair<?>中的类型。

注:

当然,你完全可以直接使用swapHelper,这里只是为了说明这样一种捕获机制。

只允许捕获单个、确定的类型,如:ArrayList<Pair<?>> 是无法使用 ArrayList<Pair<T>> 捕获的。

7、泛型的擦除与残留

泛型只在编译阶段有效,编译后类型被擦除了,也就是说jvm中没有泛型对象,只有普通对象。所以完全可以把代码编译为jdk1.0可以运行的字节码。

7.1泛型的擦除方式:

定义部分

定义部分中尖括号中间的部分直接擦除。

擦除后:

引用部分

引用部分,其中的T被替换成对应的限定类型.

擦除后:

没有限定类型:

如果没有限定类型,替换为object,

擦除后

有多个限定符

有多个限定符的,替换为第一个限定类型名。如果引用了第二个限定符的类对象,编译器会在必要的时候进行强制类型转换。

擦除后:

而表达式返回值返回时,泛型的编译器自动插入强制类型转换。

7.2泛型擦除的残留

反编译GenericClass:

好像前面说的不对啊,这还是T啊,没有擦除呀?
这就是擦除的残留。反汇编:

其中:
descriptor:对方法参数和返回值进行描述; signature:泛型类中独有的标记,普通类中没有,JDK5才加入,标记了定义时的成员签名,包括定义时的泛型参数列表,参数类型,返回值等;

可以看到public T field1;是签名,还保留了定义的格式;其对应的参数类型是Ljava/lang/Object;。

最后一行是类的签名,可以看到T后面有跟了擦除后的参数类型:<T:Ljava/lang/Object;>。
这样的机制,对于分析字节码是有意义的

7.3擦除的冲突

从擦除的机制得知,擦除后的class文件为:

发现重载无效了。这是泛型擦除造成的,无论是否在setName(String)是否标注为@Override都将是重写,都不是重载。而且,即便你不写setName(String)方法,编译器已经默认重写了这个方法。

8、泛型的约束和限制

不能使用8个基本类型实例化类型参数

原因在于类型擦除,Object不能存储基本类型:

byte,char,short,int,long,float,double,boolean

从包装类角度来看,或者说三个: Number(byte,short,int,long,float,double),char,boolean

类型检查不可使用泛型

不能创建泛型对象数组

可以定义泛型类对象的数组变量,不能创建及初始化。

注,可以创建通配类型数组,然后进行强制类型转换。不过这是类型不安全的。

不可以创建的原因是:因为类型擦除的原因无法在为元素赋值时类型检查,因此jdk强制不允许。

不能实例化泛型对象

解决办法是传入Class<T> t参数,调用t.newInstance()。

不能在泛型类的静态域中使用泛型类型

但是,静态的泛型方法可以使用泛型类型:

9、泛型的反射

java<T>泛型的更多相关文章

  1. java泛型<? extends E> 有上限通配符与<? Super E>有上限通配符

    通配符?,?表示占位,表明将来使用的时候在指明类型 <?>无限定的通配符, 是让泛型能够接受未知类型的数据 <? extends E> 有上限通配符,能够接受指定类及其子类类型 ...

  2. Java泛型-通配符的上限和下限问题

    Java的泛型中,通配符可以设置上限和下限. 上限:<? extends T> ?是T和T的子类 下限:<? super T> ?是T和T的父类 怎么看待这个上限和下限呢 首先 ...

  3. Java泛型 通配符? extends与super

    Java 泛型 关键字说明 ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类 <? super T> 表示类型下界(Java ...

  4. JAVA 泛型 通配符? extends super限定,实例区分extends super限定的作用用法

    java泛型中的关键字 ? 表示通配符类型 <? extends T> 既然是extends,就是表示泛型参数类型的上界,说明参数的类型应该是T或者T的子类. <? super T& ...

  5. 浅谈Java泛型之<? extends T>和<? super T>的区别

    关于Java泛型,这里我不想总结它是什么,这个百度一下一大堆解释,各种java的书籍中也有明确的定义,只要稍微看一下就能很快清楚.从泛型的英文名字Generic type也能看出,Generic普通. ...

  6. java 泛型详解(普通泛型、 通配符、 泛型接口)

    java 泛型详解(普通泛型. 通配符. 泛型接口) JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能---- ...

  7. Java泛型中extends和super的理解(转)

    E – Element (在集合中使用,因为集合中存放的是元素) T – Type(Java 类) K – Key(键) V – Value(值) N – Number(数值类型) ? – 表示不确定 ...

  8. java 泛型详解(普通泛型、 通配符、 泛型接口,泛型数组,泛型方法,泛型嵌套)

    JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能-----Java的泛型.  1.Java泛型  其实Java ...

  9. Java泛型中<? extends E>和<? super E>的区别

    这篇文章谈一谈Java泛型声明<? extends E>和<? super E>的作用和区别 <? extends E> <? extends E> 是 ...

  10. Java泛型中extends和super的理解

    作者:zhang siege链接:https://www.zhihu.com/question/20400700/answer/91106397来源:知乎著作权归作者所有.商业转载请联系作者获得授权, ...

随机推荐

  1. 十八:jinja2之include标签

    用于将页面的某一块地方抽取出来,要嵌入内容的时候使用,继承的概念 把具体内容分别放到其他地方同一管理,要用的时候使用include继承 使用include的时候可以直接使用接收的数据

  2. Linux常用命令:修改文件权限chmod 754/744

    常用命令:chmod 777  文件或目录 chmod  777 /etc/squid 运行命令后,squid文件夹(目录)的权限就被修改为777(可读可写可执行). Linux系统中,每个用户的角色 ...

  3. fixture详细介绍-作为参数传入,error和failed区别

    前言 fixture是pytest的核心功能,也是亮点功能,熟练掌握fixture的使用方法,pytest用起来才会得心应手! fixture简介 fixture的目的是提供一个固定基线,在该基线上测 ...

  4. 使用boost库获取文件夹下所有文件名字

    最近整理项目发现一个曾经找了好久的有用的代码片段,就是获取文件夹下所有文件的名字,和当前文件的绝对路径. 记录一下. 使用的是boost库, #include <boost/filesystem ...

  5. dapper使用时性能优化

    数据库中类型 Area 数据库类型 varchar dapper 来操作数据库,不能直接写 sql      Area=@Area)   //dapper 对C#中的字符串类型 默认是对应数据库nva ...

  6. ibatis使用iterate实现批量插入insert正确写法

    由于想批量入库提升效率,最近实现了ibatis的批量插入,结果一直报错 :StringIndexOutOfBoundsException ,原来是value中的格式不正确. 本人邮箱:techqu@1 ...

  7. Go语言mgo使用情况

    文重点介绍mgo使用,仅简单介绍mongodb. mongodb特性   mongdb简单介绍 注意: 上图已经告知我们mongo不支持事务,在开发项目应用时,想要保证数据的完整性请考虑关系型数据库( ...

  8. springboot-elasticsearch项目启动报错:'elasticsearchTemplate' that could not be found

    解决: 将elasticsearch的相关配置加入到application.yml配置文件中就可以解决

  9. DJANGO MODEL FORMSETS IN DETAIL AND THEIR ADVANCED USAGE

    Similar to the regular formsets, Django also provides model formset that makes it easy to work with ...

  10. composer配合github发布管理代码包

    前言 今日使用composer结合github管理代码包过程,方便日后需要,特此记录 流程 1 最大同性交友网站github创建自己项目,在自己项目新增composer.json文件 2 compos ...