Java 8 使用 lambda 访问私有成员?

Java 8 access private member with lambda?

Invoke private method with java.lang.invoke.MethodHandle gives an answer to private member access, while Java access bean methods with LambdaMetafactory 给出了基于 lambda 的成员访问的答案。但是,通过结合两者,我仍然找不到通过 lambda 访问私有成员的方法。错误:

Caused by: java.lang.IllegalAccessException: member is private: XXX from ZZZ
at java.lang.invoke.MethodHandles$Lookup.revealDirect(MethodHandles.java:1353)
at java.lang.invoke.AbstractValidatingLambdaMetafactory.<init>(AbstractValidatingLambdaMetafactory.java:131)
at java.lang.invoke.InnerClassLambdaMetafactory.<init>(InnerClassLambdaMetafactory.java:155)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:299)

指向 revealDirect which is part of metafactory 调用站点构建器。如何自定义构建器以控制其访问检查?

更新Working Solution Option #3 per Holger

的例子

关键部分是传递给 LambdaMetafactoryLookup 对象,然后调用 revealDirect

来自documententation:

Security and access checks are performed to ensure that this lookup object is capable of reproducing the target method handle. This means that the cracking may fail if target is a direct method handle but was created by an unrelated lookup object.

因此,lambda 表达式只能访问包含 lambda 表达式的 class 可访问的方法,因为 JVM 提供的 Lookup 对象将准确反映这些访问权限。

这也适用于 Java Beans 方法,因为按照惯例这些方法是 public


因此,如果您想调用 private 方法,您有以下三种选择:

  • 从有权访问它的 private 方法的声明 class 中生成 lambda 实例。当此 class 调用 MethodHandles.lookup() 时,它将获得适当的 Lookup 实例

    A class 也可以创建这样一个具有所需功能的 Lookup 实例,并将其移交给另一个(受信任的)class,后者可以使用它来执行此类反射操作。这正是执行 invokedynamic 指令时隐式发生的情况。 class 包含指向 LambdaMetaFactory 中的 bootstrap 方法的 invokedynamic 指令意味着这种信任。

    因此,使用所有普通操作,它总是 class 具有访问权限,必须为另一个 class

  • 启用访问权限
  • 从Java9开始,使用MethodHandles.privateLookupIn(Class, MethodHandles.Lookup)获取指定目标class上具有私有访问权限的方法句柄。这是根据模块访问规则检查的。对于同一模块内的访问,这应该总是成功的。
  • 使用更多黑魔法来获得合适的 Lookup 实例。您链接的问题提到了非 public Lookup.IMPL_LOOKUP。如果您接触到此实例并调用 in(declaringClass) on it, you get an instance with the desired properties. Alternatively you can create a restricted lookup object via MethodHandles.publicLookup() .in(declaringClass) and overwrite its access modifiers (the ones reported by lookupModes() 以启用完全访问权限。显然,两者都需要对不属于 public Java API.
  • 的字段进行访问覆盖