Java泛型5——泛型通配符

注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过

目录:

  • Java泛型1——概述
  • Java泛型2——泛型类
  • Java泛型3——泛型接口
  • Java泛型4——泛型方法
  • Java泛型5——泛型通配符
  • Java泛型6——类型擦除

1. 什么是通配符

在Java中,类与类之间是有继承关系的。例如,Integer 类继承自 Number 类,因此Integer 类的对象可以赋值给 Number 类的引用(即向上转型,这是可以实现多态性的一个重要因素)。但集合之间没有这种关系,也就是 LinkedListLinkedList之间并没有继承关系,它们都是LinkedList。因此 LinkedList 的集合并不能存放Integer 类型的变量(虽然Integer 类继承自 Number 类)。

比如下面这段代码:

public class Main {
    private static void dealTest(Test<Father> test) {
        System.out.println(test.getClass());
    }

    public static void main(String[] args) {
        Test<Father> tesF = new Test<>();
        Test<Child> testC = new Test<>();

        Main.dealTest(tesF);
        Main.dealTest(testC);//编译器报错
    }
}

class Father {

}

class Child extends Father {

}

class Test<T> {

}

这段代码定义了一个泛型类 Test ,并定义了一个处理该类的静态方法 dealTest(),该类接受一个 Test 对象作为参数。根据上面的介绍可以知道,虽然ChildFather 的子类,但TestTest 之间并没有继承关系,因此静态方法 dealTest()不能接受一个 Test 对象。因此,若想让静态方法 dealTest()处理Test 对象,还需对其进行重载:

private static void dealTest(Test<Child> test) {
    
}

但这显然违背了多态性的设计理念,而且这是编译器不允许的做法。如果能让 dealTest()方法不只接受Test对象,而是让其接受Test对象,其中XXX代表某一类型内的类型参数,那么上面这个问题就可以解决了。

这就是泛型通配符的作用:泛型通配符用于限制类型参数的范围。

泛型通配符有 3 种形式:

  • :无界通配符
  • :有上界的通配符
  • :有下界的通配符

2. 无界通配符

2.1 无界通配符的定义和使用

无界通配符:,其中 ? 代表了任意一种数据类型。比如,下面这段代码:

public class Main {
    private static void dealTest(Test<?> test) {//?代表可以使用任意一种数据类型
        System.out.println(test.getClass());
    }

    public static void main(String[] args) {
        Test<Father> tesF = new Test<>();
        Test<Child> testC = new Test<>();
        Test<Integer> testI = new Test<>();

        Main.dealTest(tesF);
        Main.dealTest(testC);
        Main.dealTest(testI);
    }
}

class Father {

}

class Child extends Father {

}

class Test<T> {

}

由于使用了无界通配符,dealTest()方法可以接受具有不同泛型参数的泛型类 Test的对象。

2.2 无界通配符注意事项

  • 不要混淆 TestTestObject 也是一种数据类型,因此 Test 代表参数类型只能为ObjectTest类, Test代表参数类型可以是任意数据类型的Test

  • 不要在实例化泛型类的时候使用无界通配符 集合的数据类型是不确定的,因此我们只能往集合中添加null,而且从其中取出的元素也只能赋值给Object对象**(无界通配符不能写也不能读)**

    import java.util.LinkedList;
    
    public class Main {
        public static void main(String[] args) {
            LinkedList<?> list = new LinkedList<>();
    
            list.add(null);
            list.add(1);//编译器报错,只能放入null
        }
    }
    
  • 2. 上界通配符

    2.1 上界通配符的定义和使用

    上界通配符:,其中T 代表了类型参数的上界。上界通配符将类型参数限制为特定类型T及其子类型。比如,表示类型参数可以是Number以及Number的子类。

    可以使用上界通配符对上面的代码进行改写:

    public class Main {
        private static void dealTest(Test<? extends Father> test) {// 代表可以使用 Father 及其子类作为类型参数
            System.out.println(test.getClass());
        }
    
        public static void main(String[] args) {
            Test<Father> fatherTest = new Test<>();
            Test<Child> childTest = new Test<>();
    
            Main.dealTest(fatherTest);
            Main.dealTest(childTest);
        }
    }
    
    class Father {
    
    }
    
    class Child extends Father {
    
    }
    
    class Test<T> {
    
    }
    

    2.2 上界通配符使用场景

    上界通配符可以用于定义泛型类、泛型接口、泛型方法(注意是定义泛型方法)以及声明变量

    import java.util.LinkedList;
    
    public class Main {
        private static <T extends Number> void dealTest(T t) {//定义泛型方法
        }
    
        public static void main(String[] args) {
            LinkedList<? extends Number> list1 = new LinkedList<Integer>();//声明变量
        }
    }
    
    class TestClass<T extends Number> {//定义泛型类
    
    }
    
    interface TestInterface<T extends Number> {//定义泛型接口
    
    }
    

    2.3 上界通配符只能读不能写

    LinkedList 为例,在使用上界通配符的时候需要注意:

    LinkedList 可以代表 LinkedListLinkedList……但是,不能指定 LinkedList的数据类型。

    import java.util.LinkedList;
    
    public class Main {
        public static void main(String[] args) {
            LinkedList<Integer> list = new LinkedList<>();
            list.add(10);
    
            LinkedList<? extends Number> list1 = list;
            
            list1.get(0);//上界通配符可以读
            list1.add(20);//编译器报错,上界通配符不可以写
            list1.add(null);//可以写入null
        }
    }
    

    在看上面的代码之前,需要明确一个概念,那就是 LinkedList表示这个集合可能是 LinkedList也可能是 LinkedList,但它什么都可能是的后果就是它什么也不是,也就是说不能确定它到底是 LinkedList还是 LinkedList或者其他什么集合,所以也就不能往里面添加具体类型的元素,因为不能确定它是什么类型的。但和无界通配符类似,可以往里面添加null

    3. 下界通配符

    3.1 下界通配符的定义和使用

    下界通配符:,与上界通配符刚好相反,T代表了类型参数的下界。类似地,下界通配符将类型参数限制为特定类型T及其超类。比如,表示类型参数可以是Integer以及Integer的超类。

    可以使用下界通配符对上面的代码进行改写:

    public class Main {
        private static void dealTest(Test<? super Child> test) {// 代表可以使用 Child 及其超类作为类型参数
            System.out.println(test.getClass());
        }
    
        public static void main(String[] args) {
            Test<Father> fatherTest = new Test<>();
            Test<Child> childTest = new Test<>();
    
            Main.dealTest(fatherTest);
            Main.dealTest(childTest);
        }
    }
    
    class Father {
    
    }
    
    class Child extends Father {
    
    }
    
    class Test<T> {
    
    }
    

    3.2 下界通配符使用场景

    下界通配符只可以用于声明变量,不可以用于定义泛型类、泛型接口、泛型方法(注意是定义泛型方法)。

    import java.util.LinkedList;
    
    public class Main {
        private static <T super Number> void dealTest() {//编译器报错,不可以用于定义泛型方法
        }
    
        private static void dealTest(TestClass<? super Number> t) {//注意,这不是泛型方法
    
        }
    
        public static void main(String[] args) {
            LinkedList<? super Number> list1 = new LinkedList<Number>();//声明变量
        }
    }
    
    class TestClass<T super Number>{//编译器报错,不可以用于定义泛型类
    
    }
    
    interface TestInterface<T super Number>{//编译器报错,不可以用于定义泛型接口
    
    }
    

    3.3 下界通配符只能写不能读

    上界通配符只能读不能写,而下界通配符正好相反,可以写但不能读。

    带有下界通配符的集合LinkedList可以添加Number类及其子类的对象。因为LinkedList最低也是个LinkedList集合,因此至少也能存放Number类对象,所以也可以存放Number子类对象。但是LinkedList的上限不知道,所以类似于带有上界通配符的集合不能存放具体的数据类型,LinkedList不能存放Number父类的对象。

    import java.util.LinkedList;
    
    public class Main {
        public static void main(String[] args) {
            LinkedList<? super Integer> list = new LinkedList<Number>();
    
            list.add(10);//下界通配符可以写
            list.add((Object) 2.5);//编译器报错,不能存放 Integer 类的父类对象
    
            Object a = list.get(0);//可以赋值给 Objedct 类型的对象
            int b = list.get(0);//编译器报错,不能赋值给除 Object 类外的其他类的对象
        }
    }
    

    下界通配符获取到的元素不能赋值给具体的类型(原因和上界通配符只能读不能写类似),但和无界通配符类似,可以赋值给Object对象

    你可能感兴趣的:(Java,SE学习笔记,java,泛型)