• 泛型的通配符


    在这里插入图片描述

    一、泛型的上界

    class Myclass<类型参数 extends 类型上界> {
    
    }
    
    • 1
    • 2
    • 3

    类型的上界决定了泛型的范围。

    class MyArray<T extends Number> {
        
    }
    public static void main(String[] args) {
            MyArray<Integer> myArray = new MyArray<>();
            MyArray<Boolean> myArray1 = new MyArray<Boolean>();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    我们发现指定了泛型的上界为数值类Number时,传入Boolean类型就会报错。
    如果没有指定类型的边界,可以认可 T extends Object,当指定了某个类型为上界,那么只接受某类型本身和子类型作为E的类型实参

    上界为接口

    我们要实现一个类去找数组的一个的最大值
    在这里插入图片描述
    这里报错了,因为我们这里是Object类型,如果是类型实参是对象的话不就能这样比较了,所以我们将上界定为Comparable接口。

    class MyArray<T extends Comparable<T>> {
       
        public T findMax(T[] arr) {
            T max = arr[0];
            for (int i = 1; i < arr.length - 1; i++) {
                if (arr[i].compareTo(max) > 0) {
                    max = arr[i];
                }
            }
            return max;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们来求一个Integer类型的最大值。

    public static void main(String[] args) {
            MyArray<Integer> myArray = new MyArray<>();
            Integer[] arr = {1,4,6,3,2};
            System.out.println(myArray.findMax(arr));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    我们来求一个Person类的最大值。

    在这里插入图片描述
    我们发现使用Person作为类型实参,未法创建对象,因为我们将泛型上界设置为了实现了Comparable的类类型。

    class Person implements Comparable<Person>{
        public int age;
    
        public Person(int age) {
            this.age = age;
        }
    
        @Override
        public int compareTo(Person o) {
            return this.age - o.age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    '}';
        }
    }
    public static void main(String[] args) {
            MyArray<Person> myArray = new MyArray<>();
            Person[] per = {new Person(15),new Person(18),new Person(20)};
            System.out.println(myArray.findMax(per));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    二、泛型方法

    1 静态方法

    刚刚我们实现的求某个类型数组的最大值必须得创建一个对象,那我们可以不创建对象直接调用吗?
    在这里插入图片描述
    我们直接给方法加上static时,系统报错了,因为static方法是在类加载指定,擦除机制是在编译时检查和强制转换,在类加载时,并不知道是什么类型,所以不能直接加static。
    静态方法语法:

    public static<泛型形参> T findMax(T[] arr)
    
    • 1
    class MyArray {
        public static<T extends Comparable<T>>  T findMax(T[] arr) {
            T max = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (arr[i].compareTo(max) > 0) {
                    max = arr[i];
                }
            }
            return max;
        }
    }
    public static void main(String[] args) {
            Integer[] arr = {1,5,4,7,9,2};
            Integer max = MyArray.findMax(arr);
            System.out.println(max);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    这样我们就可以不依赖对象求出最大值了,那类型实参在那里传?
    在这里插入图片描述
    一般在引用点的后面,一般可以省略。

    2 普通方法

    静态方法语法:

    public <泛型形参> T findMax(T[] arr)
    
    • 1
    class MyArray {
        public <T extends Comparable<T>>  T findMax(T[] arr) {
            T max = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (arr[i].compareTo(max) > 0) {
                    max = arr[i];
                }
            }
            return max;
        }
    }
    public static void main(String[] args) {
            Integer[] arr = {1,5,4,7,9,2};
            MyArray myArray = new MyArray();
            System.out.println(myArray.findMax(arr));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    三、通配符

    什么是通配符:? 用于在泛型的使用

    1 通配符的引入

    class Data<T> {
        public T data;
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    public static void main(String[] args) {
            Data<Integer> data = new Data<>();
            data.setData(18);
            print(data);
        }
        public static void print(Data<Integer> data) {
            System.out.println(data.getData());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述
    如果我们想输入类型实参为String类型呢?重写一个方法吗?支持重载吗?
    在这里插入图片描述
    我们当写入两种不同泛型类型的方法时,系统报错了,因为在运行阶段是没有泛型这个概念的,相当于他们的类型都是Data类型。
    我们来验证一下,打印两种不同泛型实参类型的对象:

    public static void main(String[] args) {
            Data<Integer> data = new Data<>();
            Data<String> data1 = new Data<>();
            System.out.println(data);
            System.out.println(data1);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    我们发现打印出来对象的类型是一样的。

    2 通配符的使用

    我们需要的解决方案:可以接收所有的泛型类型,但是又不能够让用户随意修改。这种情况就需要使用通配符"?"来处理

    public static void main(String[] args) {
            Data<Integer> data = new Data<>();
            data.setData(10);
            print(data);
            Data<String> data1 = new Data<>();
            data1.setData("woyaojindachang");
            print(data1);
        }
        //此处?代表可以接受任意类型,但由于类型不确定,所以无法修改
        public static void print(Data<?> data) {
            System.out.println(data.getData());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    3 通配符的上界

    ? extends 类:设置泛型上限

    class Data<T> {
        public T data;
        
        public T getData() {
            return this.data;
        }
        
        public void setData(T data) {
            this.data = data;
        }
    }
    public class Test {
        public static void main(String[] args) {
            Data<Integer> data = new Data<>();
            data.setData(10);
            print(data);
            Data<String> data1 = new Data<>();
            data1.setData("woyaojindachang");
            print(data1);//String不属于Number的子类
        }
        public static void print(Data<? extends Number> data) {
            System.out.println(data.getData());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述
    这里我们设置通配符的上界为Number,所以无法传入String。

    在这里插入图片描述
    因为此时通配符里可以接受任意类型,由于类型不确定,所以无法修改。

    public static void print(Data<? extends Number> data) {
            Number number = data.getData();
        }
    
    • 1
    • 2
    • 3

    因为data存储的是Number和它的子类,所以可以进行读取数据。

    4 通配符的下界

    ? super 类:设置泛型下限

    public static void fun(Data<? super Integer> data) {
            data.setData(10);
        }
    
    • 1
    • 2
    • 3

    可以传入的实参的类型是Integer或者Integer的父类类型,此处可以修改,因为添加的是Integer或者它的父类。
    在这里插入图片描述
    但不能接收,因为无法确定是那个父类。

    四、拆箱和装箱

    手动装拆箱:

    public static void main(String[] args) {
            //装箱
            Integer a = Integer.valueOf(10);
            Integer b = new Integer(10);
            //拆箱
            int c = a.intValue();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    每次手动装拆箱太过于麻烦,系统提供自动装拆箱。

    public static void main(String[] args) {
            //自动装箱
            Integer a = 10;
            Integer b = (Integer)10;
            //自动拆箱
            int j = a;
            int k = (int)a;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    我们打开字节码看一下,发现系统自动在编译阶段调用了Integer.valueof和Integer.intValue方法

    public static void main(String[] args) {
            Integer a = 127;
            Integer b = 127;
            Integer c = 200;
            Integer d = 200;
            System.out.println(a == b);
            System.out.println(c == d);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    经典面试题

    上面这段代码会输出什么呢?
    在这里插入图片描述
    在这里插入图片描述
    我们可以发现当数值在一定的范围内时,返回的是固定引用,只有超出范围才会new 新对象。
    在这里插入图片描述
    在这里插入图片描述
    我们可以得出 i 是介于-128 - 127之间的。
    在这里插入图片描述
    cache数组存储着这些引用,并且final不可修改。
    Byte、Short、Integer、Long这四种包装类型默认创建了[-128,127]的相应类型缓存数据,Character创建了数值在[0,127]范围的缓存数据,Boolean直接返回true或者false。

  • 相关阅读:
    计算机网络作业(存储单位k、KB、MB、GB、TB、PB;手机运行内存和内存的区别)
    2023-计算机保研经历
    python力扣刷题——翻转二叉树、对称二叉树(递归法、迭代法)
    德思特分享丨一文带你了解ADC测试参数有哪些?
    164-Angular项目和NodeExpress服务器发布(二)
    Java设计模式之亨元模式(Flyweight Pattern)
    3.rsync备份案例
    JSX基础语法
    Gartner权威报告解读:探究应用可观测性的发展
    QT 获取随机颜色值设置label背景色 代码
  • 原文地址:https://blog.csdn.net/buhuisuanfa/article/details/126871214