Skip to content

Instantly share code, notes, and snippets.

@srosenberg
Last active May 12, 2017 17:46
Show Gist options
  • Save srosenberg/9c134044dfaee5bb476ea1efa0c91d76 to your computer and use it in GitHub Desktop.
Save srosenberg/9c134044dfaee5bb476ea1efa0c91d76 to your computer and use it in GitHub Desktop.
scala bytecode complexity

Two ways of checking nullity: slick and pragmatic

Let's consider two somewhat contrived implementations of checking nullity of a field dereference chain in Scala and Java. Note that the use of Try below is an anti-pattern because it's essentially equivalent to using try/catch in Java as a nullity check. We use it for illustrative purposes since its idiomatic use-case involves case matching.

import scala.util._

class X {
  val x = new X
  val y: X = null

  Try(x.y.x) match {
    case Success(x) => println(x)
    case Failure(x) => println("null")
  }
}
class X {
  X x = new X();
  X y = null;

  X() {
    if (x != null && x.y != null) {
        System.out.println(x.y.x);
    } else {
        System.out.println("null");
    }
  }
}

Below are source file sizes. Scala wins by whole 3-bytes, game on.

166 May 11 12:58 X.scala
169 May 11 12:55 X.java

Bytecode complexity

Result of javac X.java,

530 May 11 12:55 X.class

Result of scalac X.scala,

858 May 11 12:46 X$$anonfun$1.class
1484 May 11 12:46 X.class
855 May 11 12:17 X$$anonfun$2.class

From the above, scalac generated 3197 bytes worth of bytecodes, around 6x increase from 530 bytes generated by javac.

Let's compare the bytecodes for the main block of code. javac's version is 64 lines whereas scalac's version is 112 lines, almost double. Note that scala's version includes instructions like instanceof and checkcast which are used to implement case-matching.

X();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #2                  // class X
       8: dup
       9: invokespecial #3                  // Method "<init>":()V
      12: putfield      #4                  // Field x:LX;
      15: aload_0
      16: aconst_null
      17: putfield      #5                  // Field y:LX;
      20: aload_0
      21: getfield      #4                  // Field x:LX;
      24: ifnull        56
      27: aload_0
      28: getfield      #4                  // Field x:LX;
      31: getfield      #5                  // Field y:LX;
      34: ifnull        56
      37: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      40: aload_0
      41: getfield      #4                  // Field x:LX;
      44: getfield      #5                  // Field y:LX;
      47: getfield      #4                  // Field x:LX;
      50: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      53: goto          64
      56: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      59: ldc           #8                  // String null
      61: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      64: return
 public X();
    Code:
       0: aload_0
       1: invokespecial #21                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #2                  // class X
       8: dup
       9: invokespecial #22                 // Method "<init>":()V
      12: putfield      #14                 // Field x:LX;
      15: aload_0
      16: aconst_null
      17: putfield      #17                 // Field y:LX;
      20: getstatic     #28                 // Field scala/util/Try$.MODULE$:Lscala/util/Try$;
      23: new           #30                 // class X$$anonfun$1
      26: dup
      27: aload_0
      28: invokespecial #33                 // Method X$$anonfun$1."<init>":(LX;)V
      31: invokevirtual #37                 // Method scala/util/Try$.apply:(Lscala/Function0;)Lscala/util/Try;
      34: astore_1
      35: aload_1
      36: instanceof    #39                 // class scala/util/Success
      39: ifeq          70
      42: aload_1
      43: checkcast     #39                 // class scala/util/Success
      46: astore_2
      47: aload_2
      48: invokevirtual #43                 // Method scala/util/Success.value:()Ljava/lang/Object;
      51: checkcast     #2                  // class X
      54: astore_3
      55: getstatic     #48                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
      58: aload_3
      59: invokevirtual #52                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
      62: getstatic     #58                 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
      65: astore        4
      67: goto          103
      70: aload_1
      71: instanceof    #60                 // class scala/util/Failure
      74: ifeq          104
      77: aload_1
      78: checkcast     #60                 // class scala/util/Failure
      81: astore        5
      83: aload         5
      85: invokevirtual #64                 // Method scala/util/Failure.exception:()Ljava/lang/Throwable;
      88: astore        6
      90: getstatic     #48                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
      93: aload         6
      95: invokevirtual #52                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
      98: getstatic     #58                 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
     101: astore        4
     103: return
     104: new           #66                 // class scala/MatchError
     107: dup
     108: aload_1
     109: invokespecial #68                 // Method scala/MatchError."<init>":(Ljava/lang/Object;)V
     112: athrow

Kotlin

Kotlin beats both Java and Scala with respect to null handling. Both the source code and the bytecode are most concise.

class X {
  val x: X? = X()
  val y: X? = null

  init {
    println(x?.y?.x? : "null")
  }
}
public X();
    Code:
       0: aload_0
       1: invokespecial #20                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #2                  // class X
       8: dup
       9: invokespecial #21                 // Method "<init>":()V
      12: putfield      #11                 // Field x:LX;
      15: aload_0
      16: getfield      #11                 // Field x:LX;
      19: dup
      20: ifnull        40
      23: getfield      #16                 // Field y:LX;
      26: dup
      27: ifnull        40
      30: getfield      #11                 // Field x:LX;
      33: dup
      34: ifnull        40
      37: goto          43
      40: pop
      41: ldc           #23                 // String null
      43: astore_1
      44: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
      47: aload_1
      48: invokevirtual #35                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      51: return
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment