Consider the following code in one package
abstract class AbstractEvent { }
public interface AbstractListener<E extends AbstractEvent> {
void handle(E e);
}
public class ConcreteEvent extends AbstractEvent {
}
public interface ConcreteListener extends AbstractListener<ConcreteEvent> {
}
(Note: AbstractEvent is package private.)
And some code in another package that depends on the Concrete* types.
public class LambdaTest {
public static void main(String[] args) {
ConcreteListener anon_cl = new ConcreteListener() {
public void handle(ConcreteEvent ce) {
System.out.println("ANON");
}
};
ConcreteListener lambda_cl = ce -> System.out.println("LAMBDA");
}
}
When LambdaTest is run an IllegalAccessError will be thrown when attempting to execute the invokedynamic instruction associated with the lambda expression.
Byte code for the invokedynamic will be similar to the following (package names will differ for the above classes):
INVOKEDYNAMIC handle()Lltest/example/ConcreteListener; [
// handle kind 0x6 : INVOKESTATIC java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
(Lltest/example/AbstractEvent;)V,
// handle kind 0x6 : INVOKESTATIC
ltest/lamda/LambdaTest.lambda$main$0(Lltest/example/ConcreteEvent;)V,
(Lltest/example/ConcreteEvent;)V
]
The method signature for the SAM is "(Lltest/example/AbstractEvent;)V" which is encoded as a method type in the constant pool. When that method type is processed it results in an IllegalAccessError occurs when verifying the access to the class AbstractEvent, since it is package private and in a different package to the accessing class LambdaTest.
The anonymous inner class works fine:
final class ltest.lamda.LambdaTest$1 implements ltest.example.ConcreteListener {
ltest.lamda.LambdaTest$1();
public void handle(ltest.example.ConcreteEvent);
public void handle(ltest.example.AbstractEvent);
}
The latter method is a bridge method.
The inner class spun up my the LambdaMetafactory contains no bridges:
final class ltest.lamda.LambdaTest$$Lambda$1 implements ltest.example.ConcreteListener {
private ltest.lamda.LambdaTest$$Lambda$1();
public void handle(ltest.example.ConcreteEvent);
}