泛型

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. Linux下获取安装包

    https://blog.csdn.net/xiaofeng3011/article/details/82797614 # cat /etc/yum.conf [main]cachedir=/var/ ...

  2. Python学习之表的介绍

    9.4 表的介绍 存储引擎 数据的存储方式就是存储引擎,引擎不同,数据的存储方式就不同 MySQL中比较重要的引擎: InnoDB:mysql5.6以上,默认的存储方式 ​ 支持 transactio ...

  3. python学习之内置函数(二)

    4.7.3 内置函数(2) int() str() bool() set() list():将一个可迭代对象转化为列表 tuple():将一个可迭代对象转换成元组 dic(): 通过相应的方式创建字典 ...

  4. flask url_for的用法

    from flask import Flask,url_for app = Flask(__name__) @app.route('/') def hello_world(): print(url_f ...

  5. ElasticSearch 7.3.0 查询、修改、删除 文档操作

    PUT chuyuan/_doc/ { "name":"xiaolin", , "sex":"F", "lov ...

  6. 第七次学习总结&&第五次实验报告

    一.实验目的 (1)理解抽象类与接口的使用: (2)了解包的作用,掌握包的设计方法. 二.实验要求 (1)掌握使用抽象类的方法. (2)掌握使用系统接口的技术和创建自定义接口的方法. (3)了解 Ja ...

  7. jquery ajax get 数组参数

    对一些get请求,但方法参数要求是数组或集合的,如下 public virtual ActionResult Test(List<int> ids) { return Json(" ...

  8. 【转帖】UDIMM、RDIMM、SODIMM以及LRDIMM的区别

    转载自http://www.sohu.com/a/165343889_781333. DIMM简介 DIMM(Dual Inline Memory Module,双列直插内存模块)与SIMM(sing ...

  9. 好问题:count(1)、count(*)、count(列)有什么区别?

    执行效果: 1.  count(1) and count(*) 当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count(*)用时多了! 从执行计划来看,count(1)和coun ...

  10. 洛谷 P2467 地精部落 题解

    题面 好难啊好难啊好难啊~(以后再玩魔兽的时候绝对绝对虐死他) 做完后总结了一下思路; 首先推一下以下三条性质: 1.若两个 i 与 i+1 不相邻,那么我们直接交换这两个数字就可以组成一个新的数列 ...