内部 Lambda 在 Class.getDeclaredMethods() 中返回?
Inner Lambda getting returned in Class.getDeclaredMethods()?
考虑这个 class:
public class Handler
{
private Supplier<Foo> foo;
public void handle( Bar bar )
{
foo = () -> bar.getFoo();
}
}
并考虑这个想要访问 handle() 方法的反射片段。
for( Method method : Handler.class.getDeclaredMethods() )
{
if ( method.getParameterCount() == 1 && Bar.class.isAssignableFrom( method.getParameterTypes()[0] ) )
{
// This is the method you are looking for
}
}
而不是查找
public void Handler.handle(Bar)
找到
private Foo Handler.lambda(Bar)
这显然会抛出异常:
java.lang.IllegalAccessException: class HandlerService cannot access a member of class Handler with modifiers "private static"
有人能解释一下这是怎么回事吗?
看起来 Java 将方法内部的 lambda 视为顶级声明方法。这是 Java 11 中的新内容(甚至是错误)吗?
您必须小心对已编译 class 的成员的假设。
甚至还有编译器生成的成员,它们是可访问 API 的一部分,例如默认构造函数或 enum
类型的 values()
和 valueOf(String)
方法。此外,内部 classes 和枚举类型的编译构造函数可能比源代码中可见的参数更多,并且由于 type erasure,编译方法中的签名可能与源代码。
除此之外,还可以有不同的合成成员。从 Java 1.1 到 Java 10,嵌套的 classes 可以通过合成辅助方法(在 Java 11 中已过时)访问彼此的私有成员。此外,覆盖泛型 classes 的方法或使用协变 return 类型可能会导致生成合成桥接方法。
这还不是全部。
下面的程序
import java.util.Arrays;
import java.util.stream.Stream;
public enum ShowSyntheticMembers {
;
public static void main(String[] args) {
Stream.of(ShowSyntheticMembers.class, Inner.class)
.flatMap(cl -> Stream.concat(Arrays.stream(cl.getDeclaredFields()),
Arrays.stream(cl.getDeclaredMethods())))
.forEach(System.out::println);
}
private boolean x;
class Inner {
protected String clone() {
assert x;
return "";
}
}
}
使用 JDK11:
编译时打印
private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
private static void ShowSyntheticMembers.lambda$main(java.io.PrintStream,java.lang.Object)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main[=11=](java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this[=11=]
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
编译时 运行 JDK 8 产量
private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
static boolean ShowSyntheticMembers.access[=12=]0(ShowSyntheticMembers)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main[=12=](java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this[=12=]
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
$VALUES
是编译器生成的 values()
实现的产物。
$assertionsDisabled
部分执行assert
语句。
this[=20=]
是内部 class 对其外部 this
. 的隐式引用
- return 类型
Object
的 clone()
方法是一种桥接方法。
access[=24=]0
方法有助于从内部 class 访问外部 class 的 private
字段,这是在 JDK 之前需要的 11 .
- 有意思的是,JDK11编译版本才存在的合成方法
lambda$main
是System.out::println
方法引用的一部分,但实际上这里不需要。
这是修复某些与交集类型相关的问题的副作用,因此非常特定于编译器。在源代码中将 .flatMap(…)
更改为 .<Object>flatMap(…)
将使该方法消失,即使使用此特定编译器版本也是如此。
因此,由于很多因素决定了在源代码中不可见的合成成员的存在,因此您不应该仅使用参数类型作为标准来搜索特定方法。
当您想访问public
成员时,最好使用Handler.class.getMethods()
而不是Handler.class.getDeclaredMethods()
。或使用 Handler.class.getMethod("handle", Bar.class)
直接获取预期的方法。
如果您不想将方法名称硬编码为字符串,运行时可见注释可以帮助识别正确的方法。
考虑这个 class:
public class Handler
{
private Supplier<Foo> foo;
public void handle( Bar bar )
{
foo = () -> bar.getFoo();
}
}
并考虑这个想要访问 handle() 方法的反射片段。
for( Method method : Handler.class.getDeclaredMethods() )
{
if ( method.getParameterCount() == 1 && Bar.class.isAssignableFrom( method.getParameterTypes()[0] ) )
{
// This is the method you are looking for
}
}
而不是查找
public void Handler.handle(Bar)
找到
private Foo Handler.lambda(Bar)
这显然会抛出异常:
java.lang.IllegalAccessException: class HandlerService cannot access a member of class Handler with modifiers "private static"
有人能解释一下这是怎么回事吗?
看起来 Java 将方法内部的 lambda 视为顶级声明方法。这是 Java 11 中的新内容(甚至是错误)吗?
您必须小心对已编译 class 的成员的假设。
甚至还有编译器生成的成员,它们是可访问 API 的一部分,例如默认构造函数或 enum
类型的 values()
和 valueOf(String)
方法。此外,内部 classes 和枚举类型的编译构造函数可能比源代码中可见的参数更多,并且由于 type erasure,编译方法中的签名可能与源代码。
除此之外,还可以有不同的合成成员。从 Java 1.1 到 Java 10,嵌套的 classes 可以通过合成辅助方法(在 Java 11 中已过时)访问彼此的私有成员。此外,覆盖泛型 classes 的方法或使用协变 return 类型可能会导致生成合成桥接方法。
这还不是全部。
下面的程序
import java.util.Arrays;
import java.util.stream.Stream;
public enum ShowSyntheticMembers {
;
public static void main(String[] args) {
Stream.of(ShowSyntheticMembers.class, Inner.class)
.flatMap(cl -> Stream.concat(Arrays.stream(cl.getDeclaredFields()),
Arrays.stream(cl.getDeclaredMethods())))
.forEach(System.out::println);
}
private boolean x;
class Inner {
protected String clone() {
assert x;
return "";
}
}
}
使用 JDK11:
编译时打印private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
private static void ShowSyntheticMembers.lambda$main(java.io.PrintStream,java.lang.Object)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main[=11=](java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this[=11=]
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
编译时 运行 JDK 8 产量
private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
static boolean ShowSyntheticMembers.access[=12=]0(ShowSyntheticMembers)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main[=12=](java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this[=12=]
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
$VALUES
是编译器生成的values()
实现的产物。$assertionsDisabled
部分执行assert
语句。this[=20=]
是内部 class 对其外部this
. 的隐式引用
- return 类型
Object
的clone()
方法是一种桥接方法。 access[=24=]0
方法有助于从内部 class 访问外部 class 的private
字段,这是在 JDK 之前需要的 11 .- 有意思的是,JDK11编译版本才存在的合成方法
lambda$main
是System.out::println
方法引用的一部分,但实际上这里不需要。
这是修复某些与交集类型相关的问题的副作用,因此非常特定于编译器。在源代码中将.flatMap(…)
更改为.<Object>flatMap(…)
将使该方法消失,即使使用此特定编译器版本也是如此。
因此,由于很多因素决定了在源代码中不可见的合成成员的存在,因此您不应该仅使用参数类型作为标准来搜索特定方法。
当您想访问public
成员时,最好使用Handler.class.getMethods()
而不是Handler.class.getDeclaredMethods()
。或使用 Handler.class.getMethod("handle", Bar.class)
直接获取预期的方法。
如果您不想将方法名称硬编码为字符串,运行时可见注释可以帮助识别正确的方法。