In one place in my code I call two different Java methods that take the same SAM type - java.lang.Runnable
- as an argument. For one call, I need to specify a type hint and for the other, I don’t.
What could be the reason for this?
For one call, I can simply pass an anonymous function:
foo(() => println("foo"))
And for the other, compilation fails unless I provide a type hint:
bar((() => println("bar")) : Runnable)
In the first case, the Java code being called is mine and in the other, it comes in via a dependency. So I thought it might have to do with one involving an older class file format or something like that.
But if I use javap -verbose
and look at things, both cases seem to be the same.
The actual calls are directly after each other in my code:
hello.foo(() => println(("alpha")))
context.system.scheduler.schedule(2.seconds, 5.seconds,
(() => println("beta")) : Runnable)(context.dispatcher)
The first call is to an example piece of Java that I wrote and works fine without the Runnable
type hint.
The second call is to some Akka logic and if I leave out the Runnable
type hint, compilation fails with:
type mismatch;
found : () => Unit
required: Runnable
context.system.scheduler.schedule(2.seconds, 5.seconds, () => println("foobar"))(context.dispatcher)
If I use javap
things look fairly identical for both cases.
Here is the calling Scala code, first the call to foo
and then the call to schedule
:
minor version: 0
major version: 52
ScalaSig: length = 0x3 (unknown attribute)
05 02 00
#68 = Utf8 foo
#69 = Utf8 (Ljava/lang/Runnable;)V
#70 = NameAndType #68:#69 // foo:(Ljava/lang/Runnable;)V
#71 = InterfaceMethodref #67.#70 // com/example/HelloWorldSibling.foo:(Ljava/lang/Runnable;)V
...
#104 = Utf8 schedule
#105 = Utf8 (Lscala/concurrent/duration/FiniteDuration;Lscala/concurrent/duration/FiniteDuration;Ljava/lang/Runnable;Lscala/concurrent/ExecutionContext;)Lakka/actor/Cancellable;
#106 = NameAndType #104:#105 // schedule:(Lscala/concurrent/duration/FiniteDuration;Lscala/concurrent/duration/FiniteDuration;Ljava/lang/Runnable;Lscala/concurrent/ExecutionContext;)Lakka/actor/Cancellable;
#107 = InterfaceMethodref #103.#106 // akka/actor/Scheduler.schedule:(Lscala/concurrent/duration/FiniteDuration;Lscala/concurrent/duration/FiniteDuration;Ljava/lang/Runnable;Lscala/concurrent/ExecutionContext;)Lakka/actor/Cancellable;
And then the two different Java methods:
1. The Java code written by me that doesn’t require a type hint:
minor version: 0
major version: 52
public abstract void foo(java.lang.Runnable);
descriptor: (Ljava/lang/Runnable;)V
2. The Java code, provided by a dependency on Akka, that does require a type hint:
minor version: 0
major version: 52
public default akka.actor.Cancellable schedule(scala.concurrent.duration.FiniteDuration, scala.concurrent.duration.FiniteDuration, akka.actor.ActorRef, java.lang.Object, scala.concurrent.ExecutionContext, akka.actor.ActorRef);
descriptor: (Lscala/concurrent/duration/FiniteDuration;Lscala/concurrent/duration/FiniteDuration;Lakka/actor/ActorRef;Ljava/lang/Object;Lscala/concurrent/ExecutionContext;Lakka/actor/ActorRef;)Lakka/actor/Cancellable;
If I’ve missed some crucial bit of the javap
output just follow up and I’ll add it.
BTW the compiler version involved is the one that’s part of Scala 2.13.6.