在没有使用泛型的情况下,如果要实现参数“任意化”,通常会定义成Object类型来接受,然后强制类型转换使用;
而强制类型转换有明显的缺点,就是必须要知道实际参数的具体类型的情况才可以进行转换,同时在强制转换的过程中,编译器不会报错提示的,只有在运行阶段才会出现异常,一定程度上存在安全隐患。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数的方式传递,类似于方法中的变量参数。可以用在类、接口、方法的创建中,分别简称为泛型类、泛型接口、泛型方法。
一、泛型作用
为了解决Java容器无法记忆元素类型的问题;
在早期,如果将容器的元素类型设置为 Object 类,虽然什么类都能放进去,但是有以下问题。
a.取出元素的时候必须进行强制类型转换(尽管集合在运行时里面元素的“运行时类型”不变,即元素的getClass返回的还是最初自己的类型而不是Object);
b. 如果不小心往集合里加了不相同类型的元素可能会导致类型异常(进行equals、compare比较的时候尤为明显);
c. 由于没有类型就需要在很多地方进行强制类型转换,但是这样做增加了编程的复杂度,并且代码也不美观(臃肿),维护起来也更加困难;
所以泛型的作用就是
a.使集合定义元素类型,即取出元素的时候无需进行强制类型转化了,可以直接用原类型的引用接收;
b. 一旦指定了性参数那么集合中元素的类型就确定了,不能添加其他类型的元素,否则会直接编译保存,这就可以避免了“不小心放入其他类型元素”的可能;
c. 上述保证了如果在编译时没有发出警告,则在运行时就一定不会产生类型转化异常(ClassCastException);
二、 泛型的概念
定义
Java 5开始,引入了参数化类型(Parameterized Type)的概念,使Java集合实现泛型,允许程序在创建集合时就可以指定集合元素的类型,比如List就表名这是一个只能存放String类型的List;
使用
1、定义集合元素类型
定义泛型引用一定要使用尖括号指定类型参数,例如:List list、Map等,其中的String、Integer之类的就是类型参数;
其次,使用构造器构造泛型对象的时候可以指定类型参数也可以不指定,例如:
i. List list = new List(); // 这当然是对的
ii. List list = new List<>(); // 这样对,因为List的类型参数可以从引用推断出!
2、定义泛型类、接口:
使用 定义了该泛型类和接口的类型参数
类
public class Person { ... },
接口
public interface MyGneric {
E add(E val);
Set makeSet();
...
}
3、通配符和边界
extends T> 是指 “上界通配符(Upper Bounds Wildcards)
super T> 是指 “下界通配符(Lower Bounds Wildcards)
extends T>
比如:Plate, 只能使用 Fruit 的子类来定义泛型的实现类,
如果 Apple 是 Fruit 的子类,那就有如下
Plate extends Fruit> p=new Plate(new Apple());
比如:
public Class Fruit(){}
public Class Apple extends Fruit(){}
public void test(? extends Fruit){};
测试
test(new Fruit());
test(new Apple());
test(new String()); //这个就会报错,
super T>
比如 : Plate
3.1、上下界通配符的副作用
3.1.1、上界 extends T>不能往里存,只能往外取
3.1.2、 下界 super T>不影响往里存,但往外取只能放在Object对象里
3.2、PECS原则
频繁往外读取内容的,适合用上界Extends。
经常往里插入的,适合用下界Super。
-------------------------------------------------------------------------------------------