【泛型和内部类的关系】

之前我们初步认识了泛型和内部类,相关的知识点都比较简单,感兴趣的小伙伴可以点击下面的链接去看看哦

目录

泛型和内部类的关系

泛型的通配符

引例

< ? >

上限通配符

下限通配符

类型擦除


泛型和内部类的关系

成员内部类会复用外部类的泛型参数,但静态内部类不会复用外部类的泛型参数

public class MyOutter <T>{
    //成员内部类
    private class Inner{
        public void test(T t1){
            System.out.println(t1);
        }
    }
    //静态内部类
    private static class Inner1<T>{
        public void fun(T t2){
            System.out.println(t2);
        }
    }
    public static void main(String[] args){
        MyOutter<String> outter = new MyOutter();
        MyOutter<String>.Inner inner = outter.new Inner();
        inner.test("hello");
        inner.test(123);
        MyOutter.Inner1<Integer> inner1 = new Inner1<>();
        inner1.fun(100);
    }
}

可以看到,成员内部类的泛型参数和外部类的不一致时,就会报错,而静态内部类和外部类的泛型参数无关。

泛型的通配符

引例

何为泛型通配符?我们先看下面这引例:

public class Message<T>{
    private T msg;
    public T getMsg() {
        return msg;
    }
    public void setMsg(T msg) {
        this.msg = msg;
    }
    public static void fun(Message<String> msg){
        System.out.println(msg.getMsg());
    }

    public static void main(String[] args) {
        Message<String> msg = new Message<>();
        msg.setMsg("String类型");
        fun(msg);
    }
}
//输出:String类型

上面的代码在主方法创建泛型类的对象,确定类型为 String 类型,调用 fun 方法就输出了设置的msg。可是,当我们又创建一个不是 String 类型的对象时呢?

 可以看到,当对象类型为整型时就无法调用 fun 方法了,fun方法的方法参数规定了形参的接收类型,由于泛型的强类型校验,不同的类型完全不能通过校验,要匹配不同的类型就要重载多次 fun 方法。这时就引出了通配符来解决问题。

< ? >

一般用在方法参数,表示可以接收该类所有类型的泛型变量。它只能用在方法的形参上,不可以用在类定义和方法返回值上

    public static void fun(Message<?> msg){
        System.out.println(msg.getMsg());
    }
    public static void main(String[] args) {
        Message<String> msg = new Message<>();
        msg.setMsg("String类型");
        fun(msg);
        Message<Integer> msg1 = new Message<>();
        msg1.setMsg(123);
        fun(msg1);
        Message<Double> msg2 = new Message<>();
        msg2.setMsg(15.5);
        fun(msg2);
    }
//输出:String类型
       123
       15.5

要注意,<?>只能调用对象的getter方法来获取值,不能调用setter方法来设置值,因为此时无法确实传入的对象类型

<? extends 类>  上限通配符

表示 ? 可以指代任意类型,但该类型必须是后面类的类型或者它的子类,如图String不是继承与Number类,因此无法通过fun方法接收

 设置泛型的上限依然不能调用setter方法来设置值,以上面的代码为例, ? 是接收Number及其子类,但是子类之间是不能相互转换的

 extends 可以用在泛型类的定义上,它是唯一一个可以定义在类型参数的通配符:

<? super 类> 下限通配符

此时 ?表示可以指代任意类型,但是该类型必须是后面类的父类。只能用在方法参数,不能用在类的类型参数

例:<? super String>

此时?只能是String或者Object

 此时我们就可以使用setter方法设置值,因为不论设置何种类型,规定好的下限对象可以发生天然的向上转型变为父类

类型擦除

我们之前已经讲过,泛型是作用在编译期间的一种机制,实际上运行期间是没有这么多类的,泛型就是典型的语法糖。那运行期间是什么类型呢?这里就是类型擦除在做的事情。

语法糖是一个术语,指计算机语言 中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。

类型擦除就是所有泛型类型参数,若没有设置泛型上限,则编译之后统一擦除为Object类型,若设置了泛型上限,则编译之后统一擦除为相应的泛型上限。

我们创建几个对象,来比较一下他们的类型地址:

public class Message<T >{
    private T msg;
    public T getMsg() {
        return msg;
    }
    public void setMsg(T msg) {
        this.msg = msg;
    }
    public static void fun(Message<? super String> msg){
        msg.setMsg("hello");
        System.out.println(msg.getMsg());
    }
    public static void main(String[] args) {
        Message<String> msg = new Message<>();
        Message<Integer> msg1 = new Message<>();
        Message<Double> msg2 = new Message<>();
        //.getClass()获取类型
        System.out.println(msg.getClass() == msg1.getClass());
        System.out.println(msg.getClass() == msg2.getClass());
    }
}
//输出:true
        true

这里使用到的 getClass 方法是反射中获取对象类型的方法。

我们知道 " == " 比较的是地址,可见三个对象定义时都是不同的泛型类型,编译之后的类型却是一样的,那么它们编译之后的类型是什么呢?

    public static void main(String[] args) throws NoSuchFieldException {
        Message<String> msg = new Message<>();
        Message<Integer> msg1 = new Message<>();
        Message<Double> msg2 = new Message<>();
        
        //获取一个类的属性
        Field field = msg.getClass().getDeclaredField("msg");
        Field field1 = msg1.getClass().getDeclaredField("msg");
        Field field2 = msg2.getClass().getDeclaredField("msg");

        //获取类型
        System.out.println(field.getType());
        System.out.println(field1.getType());
        System.out.println(field2.getType());
    }
//输出:
class java.lang.Object
class java.lang.Object
class java.lang.Object

可以看到,编译之后都成了 Object 类型,我们再演示一个设置了泛型上限的例子:

public class Message<T extends Number>{
    private T msg;
    public T getMsg() {
        return msg;
    }
    public void setMsg(T msg) {
        this.msg = msg;
    }

    public static void main(String[] args) throws NoSuchFieldException {

        Message<Integer> msg1 = new Message<>();
        Message<Double> msg2 = new Message<>();


        Field field1 = msg1.getClass().getDeclaredField("msg");
        Field field2 = msg2.getClass().getDeclaredField("msg");
                                    
        System.out.println(field1.getType());
        System.out.println(field2.getType());

    }
}
//输出:
class java.lang.Number
class java.lang.Number

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值