• Java-泛型-桥方法


    声明:转载自https://blog.csdn.net/hao_yan_bing/article/details/89447792

    前几天在看 Java 泛型的时候,发现了一个有趣的现象。就是在某些情况下,编译器在编译我们的类文件的时候会帮我们自动生成某些方法,称作桥方法。

    我们知道 Java 中的泛型在编译为 class 的时候会把泛型擦除,也就是说你写的 <T> 到最后 class 文件中其实都是 Object,看下面代码示例:

    public class A<T> {
        private T value;
        public T getValue() {
            return value;
        }
        public void setValue(T value) {
            this.value = value;
        }
    }
    // ------------编译后-------------
    public class A {
        private Object value;
        public Object getValue() {
            return value;
        }
        public void setValue(Object value) {
            this.value = value;
        }
    }

    可以看出,Java 中的泛型在编译后都变成了 Object,也可以说 Java 中的泛型其实是编译器为我们做了优化,虚拟机中是没有泛型的。

    那我们接着看下面这段代码:

    public class B extends A<String> {
        @Override
        public void setValue(String value) {
            System.out.println("---B.setValue()---");
        }
    }

    我们写了一个 B 类,继承自 A 类,并重写了 setValue 方法。

    我们来思考一个问题,按我们上面所说的 Java 泛型的擦除机制,实际 A 类中 setValue 方法应该是这样的:

    // A 类中的 setValue 方法
    public void setValue(Object value){
        this.value = value;
    }

    这个时候问题出来了,我们发现 B 类中的 setValue 方法参数与 A 类中的 setValue 方法参数不一样。按照 Java 重写方法的规则,B 类中的 setValue 方法实际上并没有重写父类中的方法,而是重载。

    所以实际上 B 类中应该是有两个 setValue 方法,一个自己的,一个继承来的:

    // 自己的
    public void setValue(String value){...}
    // 从父类继承的
    public void setValue(Object value){...}

    所以在某些场景,比如反射调用 B 类中的方法的时候,就有可能会调用到从父类继承的那个 setValue 方法。

    这个时候就会出现与我们意愿不一致的结果了,违反了我们重写方法的意愿了。

    当然,这种情况是不会出现的,因为 Java 编译器帮我们处理了这种情况。我们来查看 B.class 字节码文件:

    // class version 52.0 (52)
    // access flags 0x21
    // signature LA<Ljava/lang/String;>;
    // declaration: B extends A<java.lang.String>
    public class B extends A  {
      // compiled from: B.java
      // access flags 0x1
      public <init>()V
       L0
        LINENUMBER 4 L0
        ALOAD 0
        INVOKESPECIAL A.<init> ()V
        RETURN
       L1
        LOCALVARIABLE this LB; L0 L1 0
        MAXSTACK = 1
        MAXLOCALS = 1
    
      // access flags 0x1
      public setValue(Ljava/lang/String;)V
       L0
        LINENUMBER 8 L0
        GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
        LDC "---B.setValue()---"
        INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
       L1
        LINENUMBER 9 L1
        RETURN
       L2
        LOCALVARIABLE this LB; L0 L2 0
        LOCALVARIABLE value Ljava/lang/String; L0 L2 1
        MAXSTACK = 2
        MAXLOCALS = 2
    
      // access flags 0x1041
      public synthetic bridge setValue(Ljava/lang/Object;)V
       L0
        LINENUMBER 4 L0
        ALOAD 0
        ALOAD 1
        CHECKCAST java/lang/String
        INVOKEVIRTUAL B.setValue (Ljava/lang/String;)V
        RETURN
       L1
        LOCALVARIABLE this LB; L0 L1 0
        MAXSTACK = 2
        MAXLOCALS = 2
    }

    我们看到 B 类中有两个 setValue 方法,一个参数为 String 类型,一个参数为 Object 类型,参数为 Object 类型的就是 Java 编译器帮我们生成的桥方法,实际代码如下:

    public void setValue(String value){...}
    public void setValue(Object value){
        setValue((String)value);
    }

    桥方法内部其实就是调用了我们自己的 setValue 方法,这样就避免了在重写的时候我们还能调用到父类的方法。

    问题还没有完,我们接着看:

    public class B extends A<String> {
        @Override
        public String getValue() {
            return super.getValue();
        }
    }

    B 类重写了 A 类中的 getValue 方法。按照泛型的擦除,父类中的 getValue 方法返回值其实是 Object。

    所以其实编译器也帮我们生成了桥方法,这里就不贴字节码文件了,大家可以自己查看。编译后的 B 类其实是这样:

    public class B extends A {
        // 自己定义的方法
        public String getValue(){...}
        // 编译器生成的桥方法
        public Object getValue(){
            return getValue();
        }
    }

    这个时候我们发现 B 类有点颠覆我们的常识了,难道一个类中允许出现方法签名相同的多个方法?

    • 方法签名确实是方法名+参数列表
    • 我们也不能在同一个类中写两个方法签名相同的方法
    • JVM 会用方法名、参数类型和返回类型来确定一个方法,所以针对方法签名相同的两个方法,返回值类型不相同的时候,JVM是能分辨的
  • 相关阅读:
    【转】Entity Framework教程
    【转】《我的WCF之旅》博文系列汇总
    【转】无废话WCF系列教程
    【转】 wpf系列-入门
    基于Extjs的web表单设计器 第五节——数据库设计
    基于Extjs的web表单设计器 第四节——控件拖放
    基于Extjs的web表单设计器 第三节——控件拖放
    基于Extjs的web表单设计器 第二节——表单控件设计
    基于Extjs的web表单设计器 第一节
    基于Extjs的web表单设计器
  • 原文地址:https://www.cnblogs.com/xxxxxiaochuan/p/13734756.html
Copyright © 2020-2023  润新知