getDeclaredMethods() return 继承方法,如果超类是默认的

getDeclaredMethods() return inherited methods if superclass is default

我有两个类

// BaseClass.java
class BaseClass<T> {
 
   public String getTest(){
       return "one";
   }
 
   public String getTest2(T t){
       return "two";
   }
   public String getTest3(T t){
       return "three";
   }
}
 
// OverrideClass.java
public class OverrideClass extends BaseClass<Test>{
}
 

我试过运行下面的代码

// Test.java
public class Test {
   public static void main(String[] args) {
       Class<OverrideClass> overrideClass = OverrideClass.class;
       Method[] declaredMethods = overrideClass.getDeclaredMethods();
       System.out.println(Arrays.toString(declaredMethods));
   }
}

我认为它应该输出

[]

但实际上输出是

[public java.lang.String OverrideClass.getTest()]

通过字节码,我认为这是一个桥接方法,但不知道为什么会生成,如果我make BaseClass public它就会消失

  // access flags 0x1041
  public synthetic bridge getTest()Ljava/lang/String;
   L0
    LINENUMBER 1 L0
    ALOAD 0
    INVOKESPECIAL BaseClass.getTest ()Ljava/lang/String;
    ARETURN
   L1
    LOCALVARIABLE this LOverrideClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1
}

我的问题是:

  1. 如果 BaseClass 是默认的,为什么 getTest() 生成桥接方法?
  2. 为什么 getTest2()getTest3() 没有生成他们的桥接方法?这似乎与泛型有关。

感谢您的帮助!

我已经分析了这个问题,这是结果。我已经简化了问题中的示例。

这个答案处理了 OP

的第一个问题

Why getTest() generate a bridge method if BaseClass is default?

关于泛型出现的不一致的第二个问题,您可以阅读

示例 1

class BaseClass {

    public String getTest(){
        return "one";
    }

    public String getTest2(){
        return "two";
    }
    public String getTest3(){
        return "three";
    }
}


public class OverrideClass extends BaseClass{}


public class Application {

public static void main(String[] args) throws Exception {
    Class<OverrideClass> overrideClass1 = OverrideClass.class;
    Method[] declaredMethods1 = overrideClass1.getDeclaredMethods();
    System.out.println(Arrays.toString(declaredMethods1));
   }
}

使用 JDK 8 或 JDK 17 执行此操作始终具有相同的结果

[public java.lang.String OverrideClass.getTest(), public java.lang.String OverrideClass.getTest2(), public java.lang.String OverrideClass.getTest3()]

示例 2

把上面的例子修改成

public class BaseClass {

    public String getTest(){
        return "one";
    }

    public String getTest2(){
        return "two";
    }
    public String getTest3(){
        return "three";
    }
}

请注意,更改是在基础 class 上的访问修饰符中,现在是 public!

执行此操作会产生 []

的预期行为

但这不是 JDK 的错误。本来就是这样的。

说明

在 example1 中 getDeclaredMethods() 返回与 parent class 相同的方法的原因并不是因为这些方法被打印为继承的。这是因为那些是实际上属于 child class (OverrideClass).

的桥接方法

很久以前就添加了此功能,如您所见,oracle 开发人员here 的解释是

The proposal is to add bridge methods in these very rare cases to fix a problem in reflection with no other forseen fix or workaround. Specifically, we would generate a bridge method when a public method is inherited from a nonpublic class into a public class.

您还可以看到 here,Oracle 开发人员的最新评论是

The bridge methods are added in a case like this where a public class public methods from a non-public superclass to allow for the possibility of reflective access of the subclasses methods JDK-6342411).

Closing this issue as not a bug.

所以这只发生在非 public parent classes 中,因为在这种情况下,继承的 public 方法需要作为桥接方法添加到那 children class.

示例 2 中不存在桥接方法,如果您尝试使用 javap -c OverrideClass 打印出反汇编代码,您将看到以下内容

public class OverrideClass extends BaseClass {
      public OverrideClass();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method BaseClass."<init>":()V
           4: return
    }

示例 1 中存在桥接方法,如果您尝试使用 javap -c OverrideClass 打印反汇编代码,您将看到以下内容

public class OverrideClass extends BaseClass {
  public OverrideClass();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method BaseClass."<init>":()V
       4: return

  public java.lang.String getTest3();
    Code:
       0: aload_0
       1: invokespecial #7                  // Method BaseClass.getTest3:()Ljava/lang/String;
       4: areturn

  public java.lang.String getTest2();
    Code:
       0: aload_0
       1: invokespecial #11                 // Method BaseClass.getTest2:()Ljava/lang/String;
       4: areturn

  public java.lang.String getTest();
    Code:
       0: aload_0
       1: invokespecial #14                 // Method BaseClass.getTest:()Ljava/lang/String;
       4: areturn
}
  1. Why getTest() generate a bridge method if BaseClass is default?

  2. Why getTest2() and getTest3() did not generate their bridge method? this seems to be related to generics.

    这是一个错误,来自 JDK-8216196. No visibility bridges are created for generic methods 的描述:

    javac is normally adding visibility bridges for methods if they are public but declared by a package-private class:

    This is not the case if a method has a generic type:

错误已由 JDK-8203488. Remove error generation from TransTypes 修复,已向后移植到 11.0.1

示例,运行 结果

package org.example;

class BaseClass<T> {

    public String getTest() {
        return "one";
    }

    public String getTest2(T t) {
        return "two";
    }

    public String getTest3(T t) {
        return "three";
    }
}
package org.example;

public class OverrideClass extends BaseClass<Void> {
}
package org.example.other; // Note other package

import org.example.OverrideClass;

import java.lang.reflect.Method;
import java.util.Arrays;

public class OverrideClass2 extends OverrideClass {
    public static void main(String[] args) throws Exception {
        Arrays.stream(OverrideClass.class.getDeclaredMethods())
                .forEach(System.out::println);

        invokeGetTestReflectively();
        System.out.println("invoked 'getTest' reflectively");
        invokeGetTest2Reflectively();
        System.out.println("invoked 'getTest2' reflectively");
    }

    private static void invokeGetTestReflectively() throws Exception {
        Method method = OverrideClass2.class.getMethod("getTest");
        method.invoke(new OverrideClass2());
    }

    private static void invokeGetTest2Reflectively() throws Exception {
        Method method = OverrideClass2.class.getMethod("getTest2", Object.class);
        method.invoke(new OverrideClass2(), new Object[]{null});
    }
}

运行 结果

Java8、openjdk版本“1.8.0_332”
public java.lang.String org.example.OverrideClass.getTest()
invoked 'getTest' reflectively
Exception in thread "main" java.lang.IllegalAccessException: Class org.example.other.OverrideClass2 can not access a member of class org.example.BaseClass with modifiers "public"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
    at java.lang.reflect.Method.invoke(Method.java:491)
    at org.example.other.OverrideClass2.invokeGetTest2Reflectively(OverrideClass2.java:26)
    at org.example.other.OverrideClass2.main(OverrideClass2.java:15)
  • 允许反射访问,但 javac 中存在错误
Java17、openjdk版本“17.0.3”2022-04-19 LTS
public java.lang.String org.example.OverrideClass.getTest()
public java.lang.String org.example.OverrideClass.getTest2(java.lang.Object)
public java.lang.String org.example.OverrideClass.getTest3(java.lang.Object)
invoked 'getTest' reflectively
invoked 'getTest2' reflectively
  • 允许反射访问,javac中没有错误