10

I personaly like the when syntax as it causes identation to be much clearer. However I am concerned about the 'penalties' that I might be introducing by doing this.

I'm not really an expert on bytecode, but I can see that for the same 'logic', the when clause takes more bytecode operations.

Simple file with 3 different Kotlin functions

package com.whatever

fun method1(): String {
  return if (BuildConfig.DEBUG) "something" else "else"
}

fun method2(): String {
  return if (BuildConfig.DEBUG) {
    "something"
  } else {
    "else"
  }
}

fun method3(): String {
  return when (BuildConfig.DEBUG) {
    true -> "something"
    else -> "else"
  }
}

Generated bytecode

  // access flags 0x19
  public final static method1()Ljava/lang/String;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 4 L0
    GETSTATIC com/whatever/BuildConfig.DEBUG : Z
    IFEQ L1
    LDC "something"
    GOTO L2
   L1
    LDC "else"
   L2
    ARETURN
   L3
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x19
  public final static method2()Ljava/lang/String;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 8 L0
    GETSTATIC com/whatever/BuildConfig.DEBUG : Z
    IFEQ L1
   L2
    LINENUMBER 9 L2
    LDC "something"
   L3
    GOTO L4
   L1
    LINENUMBER 11 L1
    LDC "else"
   L5
    LINENUMBER 8 L5
   L4
    ARETURN
   L6
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x19
  public final static method3()Ljava/lang/String;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 16 L0
    GETSTATIC com/whatever/BuildConfig.DEBUG : Z
    ISTORE 0
   L1
    LINENUMBER 17 L1
    ILOAD 0
    ICONST_1
    IF_ICMPNE L2
   L3
    LDC "something"
    GOTO L4
   L2
    LINENUMBER 18 L2
    LDC "else"
   L5
    LINENUMBER 16 L5
   L4
    ARETURN
   L6
    MAXSTACK = 2
    MAXLOCALS = 1

Can someone point out how significant this cost is? And wether we should try to stay away from this pattern for simple operations?

Thanks

1 Answer 1

10

The only difference here is that in method3 the BuildConfig.DEBUG value gets stored in a local variable first. When decompiling the bytecode to Java, you see the following:

   @NotNull
   public static final String method2() {
      return BuildConfig.DEBUG?"something":"else";
   }

   @NotNull
   public static final String method3() {
      boolean var0 = BuildConfig.DEBUG;
      return var0?"something":"else";
   }

This is negligible.


If we expand the if/else clauses, we can construct the following:

fun method4(a: Int): String {
    if (a == 1) {
        return "1"
    } else if (a == 2) {
        return "2"
    } else if (a == 3) {
        return "3"
    } else {
        return "4"
    }
}

fun method5(a: Int): String {
    when (a) {
        1 -> return "1"
        2 -> return "2"
        3 -> return "3"
        else -> return "4"
    }
}

The decompiled bytecode for this is:

@NotNull
public static final String method4(int a) {
  return a == 1?"1":(a == 2?"2":(a == 3?"3":"4"));
}

@NotNull
public static final String method5(int a) {
  switch(a) {
  case 1:
     return "1";
  case 2:
     return "2";
  case 3:
     return "3";
  default:
     return "4";
  }
}

Thus, a simple when statement boils down to a switch statement in Java. See 'Why switch is faster than if' for a comparison between these two.

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

1 Comment

Is interesting that a when clause becomes an if clause after compiling to bytecode and decompiling again back to Java. I guess some compiler optimizations are in place for this logic. Thanks for the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.