Why does scalac need to box an `Int` in a method expecting an `Any`
Asked Answered
T

1

4

Consider the following class:

package test
class Test {
  def main(args: Array[String]): Unit = {
    val i: Int = 0
    println(i)
  }
}

The bytecode of main is:

public main([Ljava/lang/String;)V
   // parameter final  args
  L0
   LINENUMBER 4 L0
   ICONST_0
  L1
   ISTORE 2
  L2
   LINENUMBER 5 L2
   GETSTATIC scala/Predef$.MODULE$ : Lscala/Predef$;
   ILOAD 2
   INVOKESTATIC scala/runtime/BoxesRunTime.boxToInteger (I)Ljava/lang/Integer;
   INVOKEVIRTUAL scala/Predef$.println (Ljava/lang/Object;)V
  L3
   RETURN
  L4
   LOCALVARIABLE i I L1 L3 2
   LOCALVARIABLE this Ltest/Test; L0 L4 0
   LOCALVARIABLE args [Ljava/lang/String; L0 L4 1
   MAXSTACK = 2
   MAXLOCALS = 3

As can be seen, when we call println, the Int gets boxed into a java.lang.Integer. But the signature of println is:

def println(x: Any): Unit

As Int <: AnyVal <: Any, why does the Int need to get boxed in the call?

Trillbee answered 7/11, 2017 at 11:15 Comment(0)
A
2

Any can be syntactically used in Scala since the compiler auto boxes any Java primitive type (which is what Int is equivalent to) as needed. From the emitted byte code, you see that INVOKEVIRTUAL is called on a method with the following signature:

scala/Predef$.println (Ljava/lang/Object;)V

Since there is no notion of Any in Java, scalac emits method signature of Object, which in Scala is equivalent to AnyRef. Since Int extends AnyVal, we need to allocate the equivalent Java wrapper, which is Integer.

Algeciras answered 7/11, 2017 at 11:42 Comment(4)
The follow-up question is why on earth didn't they overload println, like Java has done?Trillbee
@Trillbee Java does have primitive input types for println, and they all end up producing a String using PrimitiveType.toString, i.e. Integer.toString, which avoids allocation of an Integer. I'm assuming the implementer assumed that if you want to println in production, allocating an Integer or any other wrapper would be the least of your problems.Algeciras
Except idiots like me might not think about this very much and blithely copy that style, unaware of the performance implicationsTrillbee
As I asked on the other thread, doesn't def print(): Unit = println(underlying.toString) work to stop the wrapping of underlying? IOW, doesn't the use of toString enables the compiler to see the "intention" and make the static call as it does in other similar places?Fiore

© 2022 - 2024 — McMap. All rights reserved.