泛型知识点总结

(1)类型参数是一种占位符,泛型的擦除保证了泛型代码内部无法获知类型的具体类型,也即无法使用new instanceof等,这也保证不使用泛型的旧版本可以继续工作!

(2)泛型类型只有在静态类型检查期间才出现,之后所有的泛型类型都将被擦除,替换为他们的非泛型上界,例如List<T>被擦除为List,而普通的类型变量在未指定边界的情况下将被擦除为Object.

(3)泛型仅仅是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器拦截源程序中的非法输入,编译器编译带类型说明的集合时会去掉类型信息,对于参数化得泛型类型,getClass()方法的返回值和原始类型完全一样。
(4)泛型函数允许类型参数被用来表示方法的一个或多个参数之间的依赖关系,或者参数与其返回值的依赖关系。如果没有这样的依赖关系,不应该使用泛型方法。

(5)Object[]数组可以是任何数组的父类,或者说,任何一个数组都可以向上转型成它在定义时指定元素类型的父类的数组,这个时候如果我们往里面放不同于原始数据类型 但是满足后来使用的父类类型的话,编译不会有问题,但是在运行时会检查加入数组的对象的类型,于是会抛ArrayStoreException:

Integer[] integers=new Integer[10];
Number[] number=integers;
number[0]=new Double(10.0); //Exception in thread "main" java.lang.ArrayStoreException: java.lang.Double

(6)协变,就是父类和子类保持相同形式的变化,但是协变有时候被支持,有时候不被支持


比如,在
数组中,协变是支持的
比如
class Parent{}
class Child extends Parent{}
那么 Child[]可以
赋值给 Parent[] ,这个就是协变

但是,在
泛型中,协变就不可以
比如 虽然Child extends Parent
但是,假设有个 Test<T>,则 Test<Child>不可以赋值给Test<Parent>,这2者毫无关系

(7)Java语言的泛型基本上完全在编译器中实现的,由编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码。 这种实现称为”擦除”
(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)

(8)擦除也造成了上述问题,即不能创建泛型类型的对象,因为编译器不知道要调用什么构造函数。 如果泛型类需要构造用泛型类型参数来指定类型的对象,那么构造函数应该传入类对象,并将它们保存起来,以便通过反射来创建实例。

类型参数的目标是:

定义泛型

包含有限制类型;

通配符的目标是:

使用泛型且我们希望能够像使用普通类型那样使用泛型类型:

为了解决类型不能动态根据实例来确定的缺点,引入了“通配符泛型”,

存取原则和PECS法则

总结 ? extends 和 the ? super 通配符的特征,我们可以得出以下结论:

  • 如果你想从一个数据类型里获取数据,使用 ? extends 通配符
  • 如果你想把对象写入一个数据结构里,使用 ? super 通配符
  • 如果你既想存,又想取,那就别用通配符。

这就是Maurice Naftalin在他的《Java Generics and Collections》这本书中所说的存取原则,以及Joshua Bloch在他的《Effective Java》这本书中所说的PECS法则。

Bloch提醒说,这PECS是指”Producer Extends, Consumer Super”,这个更容易记忆和运用。

“?”代表未知类型

extends关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类

super关键字声明了类型的下界,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至Object

上界add方法受限,下界get方法受限

命名类型参数
推荐的命名约定是使用大写的单个字母名称作为类型参数。这与C++ 约定有所不同(参阅附录 A:与 C++ 模板的比较),并反映了大多数泛型类将具有少量类型参数的假定。对于常见的泛型模式,推荐的名称是:

* K —— 键,比如映射的键。
* V —— 值,比如 List 和 Set 的内容,或者 Map 中的值。
* E —— 异常类。
* T —— 泛型。

1.2. 编写泛型类要注意:

  1) 在定义一个泛型类的时候,在 “<>”之间定义形式类型参数,例如:“class TestGen<K,V>”,其中“K” , “V”不代表值,而是表示类型。

  2) 实例化泛型对象的时候,一定要在类名后面指定类型参数的值(类型),一共要有两次书写。例如:

TestGen<String,String> t=new TestGen<String,String>();

  3) 泛型中<K extends Object>,extends并不代表继承,它是类型范围限制。


  1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。

  2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。

  3、泛型的类型参数可以有多个。

  4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。

  5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");

泛型方法:

1 根据实参暗示类型参数

2 根据返回值暗示

3 直接传给类型参数

PS: 编译器可以暗示类型参数是什么!即使没有传真实类型参数或实参(编译器根据实参为我们推断类型参数的值。),但是赋值本身存在暗示。

public class TestExternal {

public static <T> T get(String str){
return (T)str;
}

public static void main(String[] args) {
int str=TestExternal.get("");
}

}

泛型函数允许类型参数被用来表示方法的一个或多个参数之间的依赖关系,或者参数与其返回值的依赖关系。如果没有这样的依赖关系,不应该使用泛型方法。

现在有一个问题:我们应该什么时候使用泛型方法,又什么时候使用通配符类型呢?

为了理解答案,让我们先看看Collection库中的几个方法。

public interface Collection<E> {

boolean containsAll(Collection<?> c);

boolean addAll(Collection<?extends E> c);

}

我们也可以使用泛型方法来代替:

public interface Collection<E> {

<T> boolean containsAll(Collection<T> c);

<T extends E>boolean addAll(Collection<T> c);

// hey, type variables can have bounds too!

}

但是,在 containsAll 和 addAll中,类型参数T 都只使用一次。返回值的类型既不依赖于类型参数(type parameter)也不依赖于方法的其他参数(这里,只有简单的一个参数)。这告诉我们类型参数(type argument)被用作多态(polymorphism),它唯一的效果是允许在不同的调用点,可以使用多种实参类型(actual argument)。如果是这种情况,应该使用通配符。通配符就是被设计用来支持灵活的子类化的,这是我们在这里要强调的


阅读Java核心技术泛型设计经验总结(1)

<wbr></wbr>

1 Java 泛型设计中的一些主要限制和使用方面错误。

<wbr></wbr>

1.1不能将泛型用在创建类型对象中,原因则是因为Java泛型中存在类型擦除的原因,所以会导致在Java虚拟机执行时,所有泛型类型都会相应的变为它的原始类型,这就意味着如果创建泛型对象则Java虚拟机会将创建的对象改变为Object这肯定不会是希望所应该有的目的。

<wbr></wbr>

1.2不能将泛型应用在基本数据类型中,原因也是因为类型擦除机制的缘故,而基本类型不属于对象,如果要将基本类型应用到泛型当中则必须使用相应的包装器来做为替代方式。

<wbr></wbr>

1.3不能将泛型用在异常捕获和用泛型来实现Throwable子类,如果这样做的会导致无法通过编译。

<wbr></wbr>

1.4泛型数组是不合法的,原因同样出于类型擦除会导致创建的数组变为Object,而如果需要达到这样的目的,则必须使用类型信息中的Class类来满足要求。如果先创建一个类型数组而后转型为Object或是先声明类型数组而后创建一个Object数组通过强制类型转换来实现类型数组都将导致失败或者是类型擦除方面的问题。

<wbr></wbr>

1.5无法将泛型用于静态的上下文中,原因同样还是出于类型擦除,如果允许这样实现则有可能使用泛型创建一个共享Swing构建或者AWT构建等一些会出现严重错误的缺陷。

<wbr></wbr>

1.6使用泛型时应注意覆盖equals等方法的问题,原因是会导致编写出的代码导致一些难于发现的错误,但是表面上却很难让进行错误的查找。

<wbr></wbr>

1.7在使用泛型时不能同时为两个接口的子类型,而这2个接口是同一个接口的不同参数,

这条限制可以理解为,如果Employee类实现了Comparable<Employee>而Manager类继承了Employee类并且实现Comparable<Manager>接口,这样一来虽然子类和父类实现接口的约束条件是不同的,但是却是是想了同样的接口而该接口的类型参数又是父子的继承关系。则会与Java虚拟机合成的桥方法可能产生一些冲突。

<wbr></wbr>

Java泛型的约束和限制的总结。经过阅读了2遍书籍和了解,感觉Java的泛型设计和C#,C++,Python(该语言经过一些了解虽然语言本身不显示包括泛型的概念,但是由于其是纯面向对象和一些语言特性,则它隐身的方法中都包含拥有泛型特点)等语言的泛型设计的不同是Java由于其历史原因包含了类型擦除的特点,所以导致了很多在其它同类语言中不存在的限制和要求,导致有些时候会出现很多难以发现和错误提示含糊不清或者难以理解的情况。


(1)泛型限制

泛型限制的语法。
extends:限制泛型类型必须为某个类的后代,包括本类型。
语法:<T extends parentClass>
这里,T为泛型类型,extends 关键字限制泛型类型必须是parentClass的后代。parentClass 指定父类的类型,也可以是接口。
在Java语言中,对类只能单继承,对接口可以多继承,如果要限制指定类型必须从某个类继承,并且实现了多个接口,则语法为:
<T extends parentClass & parentInterface1 & parentInterface2>
注意,类必须在接口前面。

(2)通配符:包括边界通配符(上界和下界通配符)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值