5

先看个例子,有一个IntegerPrinter类,里面有一个printf方法打印一个integer类型的值。

public class Main {
    public static void main(String[] args) {
        IntegerPrinter integerPrinter = new IntegerPrinter(123);
        integerPrinter.printf();
    }
}

public class IntegerPrinter {
    Integer a;
    IntegerPrinter(Integer integer) {
        this.a = integer;
    }
    void printf() {
        System.out.println("打印的值是:" + this.a);
    }
}

打印 integer类型的值是有了,现在想打印String类型的值,你会如何做呢,是创建一个打印String类型的类,还是和我一样使用泛型。

泛型

简单的说,泛型就是允许你编写可以操作各种类型数据的代码,而不需要提前指定具体的数据类型。

现在对IntegerPrinter类 进修改,

//main函数如下
Printer<Integer> integerPrinter = new Printer<Integer>(123);
        integerPrinter.printf(); //打印的值是:123

Printer<String> stringPrinter = new Printer<String>("hello,world");
        stringPrinter.printf(); //打印的值是:hello,world


public class Printer<T> {
    T data;
    Printer(T data) {
        this.data = data;
    }
    void printf() {
        System.out.println("打印的值是:" + this.data);
    }
}

再类中,多个参数的实现

Printer<String, Integer> integerPrinter = new Printer<String, Integer>("hello,world", 123);
        integerPrinter.printf();
//第一个参数打印的值是:hello,world
//第二个参数打印的值是:123

public class Printer<T, K> {
    T data;
    K data2;
    Printer(T data, K data2) {
        this.data = data;
        this.data2 = data2;
    }
    void printf() {
        System.out.println("第一个参数打印的值是:" + this.data);
        System.out.println("第二个参数打印的值是:" + this.data2);
    }
}

在方法中使用泛型

public static void main(String[] args) {
        print("张三"); //数据是:张三
        print(18); //数据是:18
        print(true);  //数据是:true
    }
    public static <T> void print(T data) {
        System.out.println("数据是:" + data);
    }

泛型上下边界(约束)

泛型上边界:指定泛型参数必须是某个类或接口的子类。
如:类型实参只准传父类或父类型的子类

public class Printer<T extends Zoon> {
    T data;

    Printer(T data) {
        this.data = data;
    }
    void printf() {
        System.out.println("类是:" + this.data.getClass());
    }
}

public class Zoon {
    String color;
}

public class Dog extends Zoon {
    String name;
}

public class Cat extends Zoon {
    String name;
}

public static void main(String[] args) {
        Printer<Cat> catPrinter = new Printer<Cat>(new Cat());
        Printer<Dog> dogPrinter = new Printer<Dog>(new Dog());
        Printer<Zoon> zoonPrinter = new Printer<Zoon>(new Zoon());
        catPrinter.printf(); //类是:class Cat
        dogPrinter.printf(); //类是:class Dog
        zoonPrinter.printf(); //类是:class Zoon

//如果强行使用会出现报错:Type parameter 'Flowers' is not within its bound; should extend 'Zoon'
Printer<Flowers> flowersPrinter = new Printer<Flowers>(new Zoon());
    }

例: 类型必须是父类中的子类,且必须实现了其中的接口

public interface Life {
}

//使Cat类 实现Life 接口
public class Cat  extends Zoon implements Life {
    String name;
}

//使Dog类,不实现 Life 接口
public class Dog  extends Zoon {
    String name;
}

// 注:类必须写到接口前面,不然会报错
public class Printer<T extends Zoon & Life> {
    T data;

    Printer(T data) {
        this.data = data;
    }
    void printf() {
        System.out.println("类是:" + this.data.getClass());
    }
}

public static void main(String[] args) {
        Printer<Cat> catPrinter = new Printer<Cat>(new Cat());
        catPrinter.printf(); //类是:class Cat
        //下面会报错:Type parameter 'Dog' is not within its bound; should implement 'Life'
        Printer<Dog> dogPrinter = new Printer<Dog>(new Dog());
    }

在List 中使用

public static void main(String[] args) {
        List<Integer> lists = new ArrayList<>();
        lists.add(123);
        lists.add(456);
        print(lists); //[123, 456]
    }


    public static void print(List<Integer> lists) {
        System.out.println(lists);
    }

在上面中可以看到list类型是Integer,现在我想让类型是String,该如何实现呢,使用Object?,或者再写一个方法? 还是什么,
为什么不能使用Object:
因为String确实是Object 的子类,但是ListString 不是 List Object的子类。
重装写一个方法确实可以实现,但这样太麻烦了。

此时就引出了通配符,符号: ? 来表示,代表可以配置任何类型。

public static void main(String[] args) {
        List<String> lists = new ArrayList<>();
        lists.add("hello");
        lists.add("world");
        print(lists); //[hello, world]

        List<Integer> lists2 = new ArrayList<>();
        lists2.add(123);
        lists2.add(456);
        print(lists2); //[123, 456]
    }


    public static void print(List<?> lists) {
        System.out.println(lists);
    }

泛型下边界:指定泛型参数必须是某个类的父类。

例如:指定的类型是Cat类,
这个时候 我们就要使用到 super了,super 关键字允许泛型参数限制为某个类型的父类或者它本身,但不能是子类。

public static void main(String[] args) {
        List<Zoon> lists = new ArrayList<>();
        lists.add(new Zoon());
        print(lists);

        List<Cat> lists2 = new ArrayList<>();
        lists2.add(new Cat());
        print(lists2);
    }
    
    //参数的类型可以是Cat,也可以是Zoon, 不可以是Dog,因为Cat类没有继承Dog类,继承代码在上中有给出。
    public static void print(List<? super Cat> lists) {
        System.out.println(lists);
    }

总结

1,使用泛型最主要的是提高代码的复用性。
2,当看到T, E, K, V这个符号的时候不用慌,他们都只是占位符,当然,这只是一种约定俗成的习惯,你也可以使用26个字母中的任何一个表示泛型。

参考

https://www.baeldung.com/java-generics
https://www.bilibili.com/video/BV1H94y1a7bJ/?spm_id_from=333....


zZ_jie
396 声望8 粉丝

虚心接受问题,砥砺前行。