Reputation: 41919
Given:
$cat build.sbt
scalaVersion := "2.12.1"
$cat src/main/scala/net/X.scala
package net
trait Foo
object X {
val a: Int with Foo = 42.asInstanceOf[Int with Foo]
println(a + a)
}
After compiling it via sbt compile
, I javap'd the output class
files:
$javap target/scala-2.12/classes/net/X\$.class
Compiled from "X.scala"
public final class net.X$ {
public static net.X$ MODULE$;
public static {};
public int a();
}
$javap target/scala-2.12/classes/net/X.class
Compiled from "X.scala"
public final class net.X {
public static int a();
}
Why does a
have the type, int
?
I had specified a type of Int with Foo
in object X
.
Upvotes: 0
Views: 82
Reputation: 15086
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
.
Upvotes: 2