Java 杂谈每日一篇JavaJava

Java中的泛型

2018-08-02  本文已影响116人  sixleaves

Java中的泛型存在一些编译器特性, 是在编译期间就将泛型代码转换成具体的类型.

泛型的概念

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型表示

Java中泛型都用大写符号表示.但一般有比较规范的表示意思

泛型的三种应用

java中有泛型类、泛型接口、泛型方法三种泛型。

使用步骤

泛型类和接口(以类为例)

泛型类

语法:

[修饰符] class 类名<泛型名称> {
  
}

示例代码:

public class TestGenericTest {


    public static void main(String[] args) {

        Student<Integer> student = new Student<Integer>();
        student.setScore(100);
        student.setName("小明");
        System.out.println(student);

    }

}

class Student<T> {

    private String name;
    private T score;

    public Student() {

    }

    public Student(String name, T score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getScore() {
        return score;
    }

    public void setScore(T score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
也就是说泛型类型并不一定要参数实参,但是如果不传入实参就起不到本来应该起的限制作用,此时泛型类型可以为任意类型

示例代码

        Student student1 = new Student("小王", 100);
        Student student2 = new Student("小陈", "100");
        Student student3 = new Student("小李", false);
        System.out.println(student1.getScore());
        System.out.println(student2.getScore());
        System.out.println(student3.getScore());

输出

100
100
false

泛型类总结

泛型方法

为什么会有泛型方法, 主要是为了应用于以下两种场景

语法:

[修饰符] <泛型名称> 返回类型 方法名(形参类型  形参列表) {

}

其中返回类型、形参类型也可以用声明的泛型类型进行修饰

示例代码:


public class TestGenericMethod {


    public static void main(String[] args) {

        Integer[] nums = new Integer[]{1,2,3,4,5,6};
        String numsStr = MyArray.toString(nums);
        System.out.println(numsStr);
    }


}

// 类不是泛型类。又想声明泛型方法
class MyArray {

    public static <T> String toString(T[] arr) {

        String str = "[";
        int i = 0;

        for (T t:arr) {

            str += t;

            if (i != arr.length - 1)
                str+= ",";
            i++;
        }

        str +="]";
        return str;


    }
}

泛型通配符

通配符上限设置

<? extends className>
假设有如下继承体系, 则Box <? extends Fruit>表示图中蓝色部分

上限设置

通配符下限设置

<? super className>
假设有如下继承体系, Box<? super Fruit> 表示的如下黄色部分, Fruit就是下限.支持的只能是Fruit或是其父类.

下限设置

以上两张图均是取自网络

具体例子

现在有四个类,分别是 Food,Fruit,Apple 和 Plate、rice。其中 Apple 继承自 Fruit,Fruit 继承自 Food,Plate 是用来盛放这些东西的容器.Rice是谷物,继承自Food

定一个泛型类Plate

class Plate<T> {
    T x;
    public Plate(){}  
    public Plate(T x){this.x = x;}
    public void set(T x){this.x=x;}
    public T get(){return x;}
}

我们能实例化这个泛型类盘子, 让它为装载水果的盘子.因为是水果盘子,所以苹果也能放进去,但是取的时候,我们只知道这个果盘装的是水果,但不知道是什么水果。因为泛型的实参是Fruit,绑定的是Fruit

        Plate<Fruit> plate = new Plate<Fruit>();
        plate.set(new Apple());
        Fruit apple = plate.get();

其实我们可以进一步限制这个盘子能装什么类型的水果, 比如限制这个盘子只能装苹果或者水果


public class Test {

    public static void main(String[] args) {
        
        Plate<? extends Fruit> plate = new Plate<Fruit>();
        Plate<? extends Fruit> plate2 = new Plate<Apple>();
        // 上限不能往外取, 编译器是禁止的.因为编译器
        // 只知道plate和plate2是 能存水果类的盘子,但是并不知道这个盘子
        // 到底是哪一种盘子,如上代码, 可能是放苹果类的盘子,也可能是水果类盘子
        // 如果是苹果类盘子,那只能放苹果类的苹果
//        plate.set(new Apple());   编译器报错
//        plate.set(new Fruit());   编译器报错
        
        Fruit f1 = plate.get();
        Fruit f2 = plate2.get();
//        Apple a1 = plate2.get();  编译器报错
        
    }

}

上限不能往外取, 编译器是禁止的.因为编译器
上述代码只知道plate和plate2是 能存水果类的盘子,但是并不知道这个盘子到底是哪一种盘子,如上代码, 可能是放苹果类的盘子,也可能是水果类盘子, 如果是苹果类盘子,那只能放苹果类的苹果。因为苹果类型是小类型, 不能往大类型,水果类型自动转,而JVM不保证强转的成功,自然就不能调用set.

总结一句话上限通配符,只能往外取,不能往里存。

下界通配符规律恰好想法, 下界通配符,能往外取,也能往里存,但是取出来会丢失类型,存进去只能存子类或者本类,不能存父类.

泛型通配符使用总结

泛型通配符使用场景

PECS 原则

如果既要存又要取, 那么就要使用任何通配符。


引用的参考文章

java 泛型详解

上一篇下一篇

猜你喜欢

热点阅读