Java SE第9章 泛型
-
1. 编译时类型检查的重要性
-
2. 使用泛型实现编译时进行类型检查
-
3. 定义泛型接口,泛型类
-
4. 派生泛型接口,泛型类的子类,实现类
-
5. 使用类型通配符
-
6. 设定类型通配符的上限
-
7. 设定类型通配符的下限
-
8. 设定泛型形参的上限
-
9. 在方法签名中定义泛型
-
10. 泛型方法和类型通配符的区别和联系
-
11. 泛型方法和方法重载
-
12. 类型推断
-
13. 擦除和转换
-
14. 泛型和数组
1. 编译时类型检查的重要性
在没有泛型之前,集合会把所有的对象当成 Object类型处理,集合会”忘记”这个对象的数据类型当取出对象后,就需要进行强制类型转换
2. 使用泛型实现编译时进行类型检查
List<String> list = new ArrayList<String>();//创建一个只想保存字符串的list集合
此时只能把字符串加入到集合里,当把其他类型对象加入到集合中时会报错
3. 定义泛型接口,泛型类
public interface List<E>
{
void add(E x);
}
//此时在创建实现这个接口的对象时就传入 E 的类型
4. 派生泛型接口,泛型类的子类,实现类
当创建了带泛型声明的接口,父类之后,使用这些接口,父类时不能再包含泛型形参
public class A extends Apple<T>{}//错
public class A extends Apple<String>{}//对
在静态方法,静态初始化块,静态变量的声明和初始化中不允许使用泛型形参,instanceof后不能使用泛型
5. 使用类型通配符
List对象不能传给 List
java泛型的设计原则是,只要代码在编译时没有出现警告,就不会遇到运行时异常
Interger[] 可以传给 Number[],说明 Java 数组支持型变,集合不支持型变
List<?> 表示元素未知的List,仅表示它是各种泛型List的父类,并不能添加元素(除null)(因为不知道啥类型),可以调用get()方法返回List<?>集合指定索引处的元素,返回值类型未知,但一定是Object子类
6. 设定类型通配符的上限
List<? extends Shape> 表示泛型形参必须是 Shape类的子类(包括Shape)
指定通配符的上限就是为了支持类型型变,这种型变方式称为协变
由于程序无法确定这个受限制的通配符的具体类型,所以无法添加对象,只能取元素
取出的元素总是上限的类型或其子类型,对于协变的泛型而言,只能调用泛型类型作为返回值类型的方法(编译器会将该方法返回值当成通配符上限的类型),而不能调用泛型类型作为参数的方法,协变只进不出
7. 设定类型通配符的下限
A<? super Foo> 表示泛型形参必须是Foo类的父类(包括Foo),这种方式叫逆变
对于逆变的泛型集合来说,编译器只知道集合元素是下限的父类型,但不确定是那种父类型,这种逆变的泛型集合能向其中添加元素,从集合中取元素只能被当作 Object类型处理(编译器无法确定取出的到底是哪个父类的对象),对于逆变来说,它只能调用泛型类型作为参数的方法,而不能调用泛型类型作为返回值的方法,逆变只进不出
//下面dest集合元素类型必须与src集合元素类型相同,或者是其父类
public class MyUtils {
public static void main(String[] args) {
ArrayList<Number> ln = new ArrayList<>();
ArrayList<Integer> li = new ArrayList<>();
li.add(5);
Integer last = copy(ln, li);
System.out.println(ln);
}
public static <T> T copy(Collection<? super T> dest, Collection<T> src){
T last = null;
for(T ele : src){
last = ele;
dest.add(ele);
}
return last;
}
}
8. 设定泛型形参的上限
public class Apple<T extends Number>{}//表明使用Apple为T形参传入参数时只能是Number或Number子类
9. 在方法签名中定义泛型
方法中定义的泛型只能在方法中使用
public static <T> void fromArrayToCollection(T[] a, Collection<T> c){}
传入参数时,会根据 Collection传入的参数确定 T的类型
10. 泛型方法和类型通配符的区别和联系
使用通配符比使用泛型方法更加清晰和准确,类型通配符既可以在方法签名中定义形参类型,也可以用于定义形参变量的类型,但泛型方法中的泛型形参必须在对应方法中显式声明
11. 泛型方法和方法重载
public class MyUtils{
public static <T> void copy(Collection<T> dest, Collection<? extends T> src){}
public static <T> T copy(Collection<? super T> dest, Collection<T> src){}
}
这两个方法中的前一个集合都是后一个集合的形参
List<Number> ln = new ArrayList<>();
List<Integer> li = new ArrayList<>();
MyUtils.copy(ln, li);
当调用方法时会引起编译错误,因为如果是一号方法,T的类型是Number,如果是二号方法,T的类型是 Integer,编译器无法确定要调用哪个方法,就会引起编译错误
12. 类型推断
Java 8 改进了泛型方法的类型推断能力,有两个方法
- 通过调用方法的上下文推断泛型的目标类型
- 在方法调用链中,将推断得到的泛型传递到最后一个方法
13. 擦除和转换
当把一个 List赋给 List,则该 List对集合元素的检查变成了泛型参数的上限(Object)
当把一个 List 类型赋给 List时,编译器仅提示”未经检查的转换“。
不要乱用泛型,泛型指定用的类型就不要乱搞
14. 泛型与数组
只能声明 **List[]**形式的数组,不能创建 **ArrayList[]**形式的数组
Java不支持创建泛型数组