泛型的通配符及擦除机制详解

 

目录

一、通配符解决什么问题

二、通配符上界

三、通配符下界  ​编辑

四、泛型类的擦除机制 


引言:

        在这篇文章中,我主要介绍前一篇泛型没介绍完整的泛型通配符和泛型的擦除机制Java中泛型的详细介绍

? 用于在泛型的使用,即为通配符

一、通配符解决什么问题

static class Message<T> {
        private T message ;
        public T getMessage() {
            return message;
        }
        public void setMessage(T message) {
            this.message = message;
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            Message<String> message = new Message<>() ;
            message.setMessage("比特就业课欢迎您");
            fun(message);
        }
        public static void fun(Message<String> temp){
            System.out.println(temp.getMessage());
        }
    }

以上程序会带来新的问题,如果现在泛型的类型设置的不是String,而是Integer.  

public class TestDemo {
        public static void main(String[] args) {
            Message<Integer> message = new Message() ;
            message.setMessage(99);
            fun(message); // 出现错误,只能接收String
        }
        public static void fun(Message<String> temp){
            System.out.println(temp.getMessage());
        }
    }

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

 示例:使用通配符

public class TestDemo {
        public static void main(String[] args) {
            Message<Integer> message = new Message() ;
            message.setMessage(55);
            fun(message);
        }
        // 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改
        public static void fun(Message<?> temp){
        //temp.setMessage(100); 无法修改!
            System.out.println(temp.getMessage());
        }
    }

二、通配符上界

<? extends 上界>
<? extends Number>//可以传入的实参类型是Number或者Number的子类

示例:

    class Food {
    }
    class Fruit extends Food {
    }
    class Apple extends Fruit {
    }
    class Banana extends Fruit {
    }
    class Message<T> { // 设置泛型
        private T message ;
        public T getMessage() {
            return message;
        }
        public void setMessage(T message) {
            this.message = message;
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            Message<Apple> message = new Message<>() ;
            message.setMessage(new Apple());
            fun(message);
            Message<Banana> message2 = new Message<>() ;
            message2.setMessage(new Banana());
            fun(message2);
        }
        // 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改
        public static void fun(Message<? extends Fruit> temp){
        //temp.setMessage(new Banana()); //仍然无法修改!
        //temp.setMessage(new Apple()); //仍然无法修改!
            System.out.println(temp.getMessage());
        }
    }
        此时无法在fun 函数中对 temp 进行添加元素,因为 temp 接收的是 Fruit 和他的子类,此时存储的元素应该是哪个子类无法确定。所以添加会报错!但是可以获取元素。

public static void fun(Message<? extends Fruit> temp){
        //temp.setMessage(new Banana()); //仍然无法修改!
        //temp.setMessage(new Apple()); //仍然无法修改!
        Fruit b = temp.getMessage();
        System.out.println(b);
    }

通配符的上界,不能进行写入数据,只能进行读取数据 

三、通配符下界  

<? super 下界>
<? super Integer>//代表 可以传入的实参的类型是Integer或者Integer的父类类型

示例:

class Food {
    }
    class Fruit extends Food {
    }
    class Apple extends Fruit {
    }
    class Plate<T> {
        private T plate ;
        public T getPlate() {
            return plate;
        }
        public void setPlate(T plate) {
            this.plate = plate;
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            Plate<Fruit> plate1 = new Plate<>();
            plate1.setPlate(new Fruit());
            fun(plate1);
            Plate<Food> plate2 = new Plate<>();
            plate2.setPlate(new Food());
            fun(plate2);
        }
        public static void fun(Plate<? super Fruit> temp){
            // 此时可以修改!!添加的是Fruit 或者Fruit的子类
            temp.setPlate(new Apple());//这个是Fruit的子类
            temp.setPlate(new Fruit());//这个是Fruit的本身
            //Fruit fruit = temp.getPlate(); 不能接收,这里无法确定是哪个父类
            System.out.println(temp.getPlate());//只能直接输出
        }
    }

 通配符的下界,不能进行读取数据,只能写入数据。

 

四、泛型类的擦除机制 

        Java 中的泛型是在编译时期进行类型检查的机制,但在编译后会被擦除。这意味着在运行时,Java 虚拟机不会知道泛型的具体类型,而是使用泛型类型的原始类型。这个过程被称为类型擦除。

        具体来说,编译器在编译泛型类或方法时,会将其中的类型参数用其上限(如果没有指定上限,则为 Object来替换,并插入必要的类型转换以确保类型安全。这样做是为了保持向后兼容性,因为泛型是在 Java 5 中引入的,而在此之前的版本中并没有泛型支持。

下面是一个简单的例子来说明泛型类的擦除机制:

import java.util.*;

public class GenericExample<T> {
    private T data;

    public GenericExample(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public static void main(String[] args) {
        GenericExample<String> stringExample = new GenericExample<>("Hello");
        GenericExample<Integer> integerExample = new GenericExample<>(123);

        // 编译后会擦除泛型类型
        System.out.println(stringExample.getClass()); // class GenericExample
        System.out.println(integerExample.getClass()); // class GenericExample

        // 因此,在运行时无法获取泛型的具体类型
        System.out.println(stringExample.getData()); // Hello
        System.out.println(integerExample.getData()); // 123

        // 编译器插入了类型转换以确保类型安全
        // 运行时会执行类型转换
        String str = stringExample.getData(); // No explicit casting needed
        Integer num = integerExample.getData(); // Explicit casting needed
    }
}

        在上面的例子中,尽管我们在实例化 GenericExample 时指定了具体的类型参数,但在运行时,通过 getClass() 方法可以看到实际的类型是 GenericExample,而不是 GenericExample<String> 或 GenericExample<Integer>。这就是类型擦除的表现。在获取数据时,编译器会自动插入类型转换以确保类型安全。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值