Consider this class:
public class Handler
{
private Supplier<Foo> foo;
public void handle( Bar bar )
{
foo = () -> bar.getFoo();
}
}
And consider this reflection snippet which wants to access the handle() method.
for( Method method : Handler.class.getDeclaredMethods() )
{
if ( method.getParameterCount() == 1 && Bar.class.isAssignableFrom( method.getParameterTypes()[0] ) )
{
// This is the method you are looking for
}
}
Instead of finding
public void Handler.handle(Bar)
It finds
private Foo Handler.lambda$3(Bar)
Which obviously then throws the Exception:
java.lang.IllegalAccessException: class HandlerService cannot access a member of class Handler with modifiers "private static"
Can someone explain what is going on here, please?
It looks like Java considers the lambda inside the method as a top-level declared method. Is this new (or even a bug) in Java 11 ?
You have to be careful with assumptions about the members of the compiled class.
There are even compiler-generated members which are part of the accessible API, like the default constructor or the values()
and valueOf(String)
methods of enum
types. Further, compiled constructors of inner classes and enum types may have more parameters than visible in the source code and due to type erasure, the signatures in the compiled methods may differ from the source code.
Besides that, there can be different synthetic members. From Java 1.1 to Java 10, nested classes may access each others private members via synthetic helper methods (which became obsolete with Java 11). Also, overriding methods of generic classes or using covariant return types may cause the generation of a synthetic bridge method.
And that’s still not all.
The following program
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 "";
}
}
}
prints when compiled with JDK 11:
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$1(java.io.PrintStream,java.lang.Object)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main$0(java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this$0
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
while compiling and running with JDK 8 yields
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$000(ShowSyntheticMembers)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main$0(java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this$0
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
$VALUES
is an artifact of the compiler generated values()
implementation.$assertionsDisabled
part of the implementation of the assert
statement.this$0
is the inner class’ implicit reference to its outer this
.clone()
method with the return type Object
, is a bridge method.access$000
method helps to access the private
field of the outer class from the inner class, which is needed prior to JDK 11.lambda$main$1
, which only exists in the JDK 11 compiled version is part of the System.out::println
method reference, but actually not needed here..flatMap(…)
to .<Object>flatMap(…)
in the source code would make the method disappear even with this specific compiler version.So, since a lot of factors determine the presence of synthetic members not visible in the source code, you should not search for a particular method by only using the parameter type as criteria.
When you want to access the public
members, you better use Handler.class.getMethods()
instead of Handler.class.getDeclaredMethods()
. Or use Handler.class.getMethod("handle", Bar.class)
to directly get the intended method.
If you don’t want to hardcode the method name as a string, a runtime visible annotation could help to identify the right method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With