This is simply the way all intersection types in Scala are compiled to JVM bytecode. The JVM has no way to represent things like Int with Foo
, so the compiler erases the type to the first "simple" type: Int
in this case. This means that if you use the value a
like a Foo
the compiler has to insert a cast into the bytecode.
Take a look at the following REPL session:
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.
scala> trait Foo { def foo = "foo" }
defined trait Foo
scala> trait Bar { def bar = "bar" }
defined trait Bar
scala> class FooBar extends Foo with Bar
defined class FooBar
scala> class Test { val foobar: Foo with Bar = new FooBar }
defined class Test
scala> object Main {
| def main(): Unit = {
| val test = new Test().foobar
| println(test.foo)
| println(test.bar)
| }
| }
defined object Main
scala> :javap -p -filter Test
Compiled from "<console>"
public class Test {
private final Foo foobar;
public Foo foobar();
public Test();
}
scala> :javap -c -p -filter Main
Compiled from "<console>"
...
public void main();
Code:
...
15: invokeinterface #62, 1 // InterfaceMethod Foo.foo:()Ljava/lang/String;
...
27: checkcast #23 // class Bar
30: invokeinterface #69, 1 // InterfaceMethod Bar.bar:()Ljava/lang/String;
...
Int with Foo
is actually a special case. Int
is a final type and Foo
is a trait. Apparently the compiler prefers final types and classes over traits. So in Foo with Bar
where Foo
is a trait and Bar
is a class, the type still gets erased to Bar
instead of Foo
.