Java SE第9章 泛型

  • Post author:
  • Post category:java




Java SE第9章 泛型




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不支持创建泛型数组



版权声明:本文为m0_61466355原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。