【泛型和内部类的关系】

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

  • 初识Java内部类
  • 初识Java泛型

目录

泛型和内部类的关系

泛型的通配符

引例

< ? >

上限通配符

下限通配符

类型擦除


泛型和内部类的关系

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

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

【泛型和内部类的关系】_第1张图片

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

泛型的通配符

引例

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

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

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

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

【泛型和内部类的关系】_第2张图片

【泛型和内部类的关系】_第3张图片

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

< ? >

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

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

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

【泛型和内部类的关系】_第4张图片

  上限通配符

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

【泛型和内部类的关系】_第5张图片

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

【泛型和内部类的关系】_第6张图片

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

下限通配符

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

例:

此时?只能是String或者Object

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

【泛型和内部类的关系】_第7张图片

【泛型和内部类的关系】_第8张图片

类型擦除

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

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

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

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

public class Message{
    private T msg;
    public T getMsg() {
        return msg;
    }
    public void setMsg(T msg) {
        this.msg = msg;
    }
    public static void fun(Message msg){
        msg.setMsg("hello");
        System.out.println(msg.getMsg());
    }
    public static void main(String[] args) {
        Message msg = new Message<>();
        Message msg1 = new Message<>();
        Message 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 msg = new Message<>();
        Message msg1 = new Message<>();
        Message 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{
    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 msg1 = new Message<>();
        Message 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

你可能感兴趣的:(Java数据结构,数据结构,java)