gpt4 book ai didi

java - 反射 - Method::get Generic Return Type no generic - 可见性

转载 作者:行者123 更新时间:2023-12-03 11:17:37 26 4
gpt4 key购买 nike

描述
我有一个奇怪的问题 Method::getGenericReturnType()无法检索泛型类型信息。
这是最小化版本:

public class Test {
public static void main(String[] args) {
Method method = B.class.getMethods()[0]; // foo() method inherited from A
System.out.println(method.getGenericReturnType());
}

static class A {
public List<String> foo() { return null; }
}

public static class B extends A {}
}
输出是
java.util.List
没有任何通用类型信息。这对我来说似乎很奇怪。
但是,更改 Apublic 的可见性它正确地给出了
java.util.List<java.lang.String>

问题
我不知道这是一个错误还是实际预期的行为。如果是预期的,其背后的原因是什么?
我正在使用来自 AdoptOpenJDK 的 OpenJDK 15:
// javac
javac 15.0.1

// java
openjdk version "15.0.1" 2020-10-20
OpenJDK Runtime Environment AdoptOpenJDK (build 15.0.1+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15.0.1+9, mixed mode, sharing)
friend 也可以复制它:
  • JDK 祖鲁 8
  • 采用OpenJDK 10、11、14

  • 发现
    我做了很多实验,发现 A 之间唯一的可见性组合和 B触发此问题的是 BpublicA不是 public .任何其他组合,它都会再次按预期工作。所以只有
  • public , 一个 protected
  • public , 一个 package-visible
  • public , 一个 private

  • 表现出奇怪的行为。
    我尝试在不同的文件中移动代码,将其放入不同的包中,添加或删除 static到处都是,没有任何改变。
    我还检查了 source code的方法,即
    public Type getGenericReturnType() {
    if (getGenericSignature() != null) {
    return getGenericInfo().getReturnType();
    } else { return getReturnType();}
    }
    在哪里 getGenericSignature()依赖于 String signatureMethod 的构建过程中设置实例。看来是 null由于某种原因在上述情况下。

    更新 1
    我刚刚检查了类的字节码,并在 B.class 中找到了它。 :
      public Test$B();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
    stack=1, locals=1, args_size=1
    0: aload_0
    1: invokespecial #1 // Method Test$A."<init>":()V
    4: return
    LineNumberTable:
    line 16: 0

    public java.util.List foo();
    descriptor: ()Ljava/util/List;
    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
    stack=1, locals=1, args_size=1
    0: aload_0
    1: invokespecial #7 // Method Test$A.foo:()Ljava/util/List;
    4: areturn
    LineNumberTable:
    line 16: 0
    对我来说,这看起来像 B ,由于某种原因,创建了另一个方法 foo()这只是将方法调用转发到 A小号 foo()因此没有通用类型信息。
    此外,当调用 B.getDeclaredMethods()它实际上返回一个方法,即
    public java.util.List Test$B.foo()
    即使此方法应该排除继承的方法(来自 documentation ):

    Returns an array containing Method objects reflecting all the declared methods of the class or interface represented by this Class object, including public, protected, default (package) access, and private methods, but excluding inherited methods.


    如果 B 现在是有意义的真的创建了一个包装方法。
    但是,它为什么要创建这样的方法?是否有解释此行为的 JLS 部分?

    最佳答案

    让我们在这里慢慢来。首先,here is why首先生成一个桥接方法。即使你放弃了泛型,仍然会有一个桥接方法。也就是说,这段代码:

    static class A {
    public String foo() { return null; }
    }

    public static class B extends A {}
    仍会生成 foo使用 ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC 的方法.您可以阅读错误描述并了解为什么需要这样做。
    另一方面,如果您制作 A public ,不会生成这样的方法,原因应该很明显,考虑到前面的bug解释(我希望)。所以这个想法是,如果你有一个非公共(public)类(class), javac将为上述场景生成一个桥接方法。

    现在,如果您将泛型添加到合成方法的组合中,事情就会开始有所启发。例如,你有这个:
    interface WithGeneric<T> {
    public WithGeneric<T> self(T s);
    }

    public class Impl implements WithGeneric<String> {

    @Override
    public WithGeneric<String> self(String s) {
    return null;
    }
    }
    Impl.class中也会产生桥接方法,但它的声明将是接口(interface)的删除。换句话说, Impl.class 中会有两种方法。 :
    public WithGeneric<String> self(String) {...}

    public WithGeneric self(Object) {...}

    如果你把这两件事粘合起来:
  • 在非公共(public)类的情况下,将创建一个桥接方法(以便反射起作用)
  • 在泛型的情况下,将创建一个删除的桥接方法(这样删除的调用就可以工作)

  • 事情会(以某种方式)有意义。

    关于java - 反射 - Method::get Generic Return Type no generic - 可见性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65613220/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com