3

I was inspecting the byte-code generated by implicit classes and wanted to compare to what is generated when they extend AnyVal.

Without implicit:

object Example1 { class Wrapper(val self: Int) extends AnyVal { def add(n: Int): Int = self + n } def foo(w: Wrapper): Wrapper = new Wrapper(w.add(42)) } 

The (relevant part of) bytecode:

scala>:javap Example1 [...] public int foo(int); descriptor: (I)I flags: ACC_PUBLIC Code: stack=3, locals=2, args_size=2 0: getstatic #19 // Field Example1$Wrapper$.MODULE$:LExample1$Wrapper$; 3: iload_1 4: bipush 42 6: invokevirtual #23 // Method Example1$Wrapper$.add$extension:(II)I 9: ireturn LocalVariableTable: Start Length Slot Name Signature 0 10 0 this LExample1$; 0 10 1 w I LineNumberTable: line 11: 3 [...] 

With implicit:

object Example2 { implicit class Wrapper(val self: Int) extends AnyVal { def add(n: Int): Int = self + n } def foo(w: Wrapper): Wrapper = w.add(42) } 

The (relevant part of) byte-code:

scala>:javap Example2 [...] public int Wrapper(int); descriptor: (I)I flags: ACC_PUBLIC Code: stack=1, locals=2, args_size=2 0: iload_1 1: ireturn LocalVariableTable: Start Length Slot Name Signature 0 2 0 this LExample2$; 0 2 1 self I LineNumberTable: line 9: 0 public int foo(int); descriptor: (I)I flags: ACC_PUBLIC Code: stack=4, locals=2, args_size=2 0: aload_0 1: getstatic #23 // Field Example2$Wrapper$.MODULE$:LExample2$Wrapper$; 4: iload_1 5: bipush 42 7: invokevirtual #27 // Method Example2$Wrapper$.add$extension:(II)I 10: invokevirtual #29 // Method Wrapper:(I)I 13: ireturn LocalVariableTable: Start Length Slot Name Signature 0 14 0 this LExample2$; 0 14 1 w I LineNumberTable: line 12: 0 [...] 

As a result of extending AnyVal the call to add is invoked on the companion object, and the type Wrapper does not show in the type signature of foo (public int foo(int);) in both versions.

Hovewer, in the second version, there is a call just before return: 10: invokevirtual #29. It calls public int Wrapper(int); which seemingly doesn't do anything. (Although I might be wrong, as I don't have much experience with reading the byte-code)

So the question is, what's the significance of that call? Can't it be omitted?

1 Answer 1

3

The problem is that your code snippets are not equivalent. An implicit class Foo is compiled/desugared into a class Foo and an implicit conversion method Foo. That's also the reason that implicit classes (currently) can't be top level.

So your first snippet should be:

object Example1 { class Wrapper(val self: Int) extends AnyVal { def add(n: Int): Int = self + n } def Wrapper(self: Int): Wrapper = new Wrapper(self) def foo(w: Wrapper): Wrapper = Wrapper(w.add(42)) } 

The compiler erases calls to value class constructors if possible. But it doesn't erase the call to the Wrapper method, implicit or not.

I guess that the JIT compiler in the JVM will eventually erase that method call anyway.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.