Котлин использует, когда для простых условий

Мне лично нравится синтаксис when, поскольку он значительно упрощает идентификацию. Однако я беспокоюсь о "штрафах", которые я мог бы сделать, делая это.

Я не специалист по байт-коду, но я вижу, что для той же "логики" предложение when принимает больше операций байт-кода.

Простой файл с тремя различными функциями Kotlin

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"
  }
}

Сгенерированный байт-код

  // 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

Может ли кто-нибудь указать, насколько значительна эта стоимость? И мы должны стараться держаться подальше от этого шаблона для простых операций?

Спасибо

Ответ 1

Единственное отличие здесь в том, что в method3 значение BuildConfig.DEBUG сначала сохраняется в локальной переменной. При декомпиляции байт-кода на Java вы увидите следующее:

   @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";
   }

Это незначительно.


Если мы разложим предложения if/else, мы можем построить следующее:

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"
    }
}

Декомпилированный байт-код для этого:

@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";
  }
}

Таким образом, простой оператор when сводится к оператору switch в Java. См. 'Почему для сравнения между этими двумя клавишами переключатель быстрее, чем если".