泛型学习总结
什么是泛型
泛型的本质是参数化类型,所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法(函数)。
泛型的作用:将运行期报错提前到编译期报错
- 在编译阶段发现问题不匹配
List list1 = new ArrayList();
list1.add(100);
for (Object o : list1) {
// 运行时报错:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
String item = (String) o;
}
// 对List实例化时,指定T类型为String(List就是泛型类List<T>)
List<String> list2 = new ArrayList<>();
list2.add("aaaa");
list2.add(100); // 编译报错:类型转换失败
再来一个典型的例子(摘自“主要参考文档1”):这个可以理解为:范型想比Objext的优势
2. 运行阶段,带不带泛型返回值都一样
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
Class classStringList = stringList.getClass(); // Class java.util.ArrayList
Class classIntegerList = integerList.getClass(); // Class java.util.ArrayList
if (classStringList.equals(classIntegerList)){
System.out.println("类型相同"); // 类型相同
}
泛型类:
1. 在实例化泛型类时,必须指定T等泛型的具体类型
// 泛型类的定义-- 一个泛型变量,同java类库中的Collection<V>、List<V>、Set<V> 和 Map<K,V>
public class Generic<T>{..}
// 泛型的实例化
Generic<Integer> genericInteger = new Generic<>(123456);
Generic<String> genericString = new Generic<>("key_vlaue");
// 泛型类的定义-- 多个泛型变量
class MorePoint<T,U,A>{
}
MorePoint<Integer, String, Boolean> point = new MorePoint<>();
2. 泛型类型使用的字母规范
任意一个字母都可以,他们的意义是完全相同的,但为了提高可读性,大家还是用有意义的字母比较好,常见的泛型模型:
E — Element,常用在java Collection里,如:List,Iterator,Set
K,V — Key,Value,代表Map的键值对
N — Number,数字
T — Type,类型,如String,Integer等等
泛型接口的实现:
1、 不传入泛型实参(不指定泛型对应的具体类型),类似于直接定义泛型类: 泛型类
class FruitGenerator<T> implements Generator<T>{
2、 传入实参(指定泛型对应的具体类型),在接口后面指定具体类型: 非泛型类
public class FruitGenerator implements Generator<String> {
泛型方法
/**
1. 泛型方法的基本介绍
2. @param tClass 传入的泛型实参
3. @return T 返回值为T类型
4. 说明:
5. 1)public与返回值中间<T>非常重要,可以理解为声明此方法为泛型方法;
6. 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
7. 2)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T --》 详见4.6.1节。
8. 3)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
类型绑定
-
任何的泛型变量(比如这里的T)都是派生自Object,所以:
(1)填充泛型变量时只能使用派生自Object的类,如String,Integer,Double等;而不能使用原始的变量类型,比如int,double,float等
(2)T只能使用Object的方法 -
T如何使用Object以外的方法,比如compare方法: 类型绑定 extends
// T是BoundingType的子类型
// T和BoundingType可以是类,也可以是接口
// ”extends“表示的子类型,不等同于继承。
<T extends BoundingType>
// 添加上extends Comparable之后,就可以Comparable里的函数了
T extends Comparable
// 使用&连接,实现多绑定
T extends Fruit & Serializable
通配符
- “T"与”?"的区别
“T”: 类型参数,声明一个泛型类或泛型方法。
“?”: 无界通配符,使用泛型类或泛型方法(即填充范型变量T等的位置) - 通配符只能用于填充泛型变量T的位置,不能用于定义变量
FanXing<?> fanXing1 = new FanXing<Integer>(1);
FanXing<?> fanXing2 = new FanXing<?>(2); // 编译报错
? x; // 编译报错
- 通配符与extends绑定的特点
a、<? extends XXX>指填充 派生于XXX的任意子类
b、如果不加以限定,在后期的使用中编译器可能不会报错
c、利用<? extends Number>定义的变量,只可取其中的值,不可修改
Point<? extends Number> point = new Point<Integer>(3,3);
Number x = point.getX();
point.setX(new Integer(5)); // 编译报错 类型转换失败
- 通配符与super绑定的特点
a、<? super XXX>表示 填充 任意XXX的父类
b、能存不能取
其它知识点
- 泛型/不是协变的、集合也不是协变的、数组是协变的
(1). Ingeter是Number的一个子类,但Generic不是Generic的子类
(2). 泛型通配符
public static void test() {
List<Integer> integerList = new ArrayList<>();
List<Number> numberList = new ArrayList<>();
integerList.add(100);
numberList.add(123);
showKeyValue(integerList); // 编译报错
showKeyValue(numberList); // OK
showKeyValue1(integerList); // 下面两个都是正常的
showKeyValue1(numberList);
}
public static void showKeyValue(List<Number> numberList) {
}
public static void showKeyValue1(List<?> numberList) {
}