Is "double exclamation" same as "as" in Kotlin?

It's different in the resulting exception:

The cast of a nullable value to its non-null type causes a TypeCastException if null:

val s: String? = null
val s2 = s as String //kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String

The double exclamation mark, double bang, means that you don't care of a type's nullability information and just want to use it as if it can't be null. In case it is, a NullpointerException is thrown:

val s: String? = null
val s2: String = s!! //Exception in thread "main" kotlin.KotlinNullPointerException

You should rarely use either of the two options and handle nullability cautiously instead:

s?.let {
    //it's safe here!
} ?: //optionally handle null case

Smart casting will help you, too.


They are basically the same thing.

In Kotlin 1.3 (or older) first one will throw KotlinNullPointerExcption for null, and second one will throw TypeCastException.

Kotlin 1.4 stops using its own exceptions and both expressions fail with regular NPE but as version has a detailed message: Exception in thread "main" java.lang.NullPointerException: null cannot be cast to non-null type kotlin.String


The difference is the exception (either KotlinNullPointerExcption or TypeCastException) you get when text == null (as @Miha_x64 has stated) in every other regard they generate the same bytecode. Also the cast and the !! are only valid in the current statement, so the behaviour is unlikely to change. I would go for !! every time, because the exception reflects the error better.

fun foo() {
    var s = ""
    val t: String? = ""

    if (t as String != "") s = "1"
    if (t!! != "") s = "2"
}

yields

public final foo()V
L0
  LINENUMBER 9 L0
  LDC ""
  ASTORE 1
L1
  LINENUMBER 10 L1
  LDC ""
  ASTORE 2
L2
  LINENUMBER 12 L2
  ALOAD 2
L3
  LDC ""
  INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z
  ICONST_1
  IXOR
  IFEQ L4
  LDC "1"
  ASTORE 1
L4
  LINENUMBER 13 L4
  ALOAD 2
L5
LDC ""
  INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z
  ICONST_1
  IXOR
  IFEQ L6
  LDC "2"
  ASTORE 1
L6
  LINENUMBER 14 L6
  RETURN
L7
  LOCALVARIABLE t Ljava/lang/String; L2 L7 2
  LOCALVARIABLE s Ljava/lang/String; L1 L7 1
  LOCALVARIABLE this Lde/leo; L0 L7 0
  MAXSTACK = 2
  MAXLOCALS = 3

The interesting part is L3 and L5


They are the same in the sense that they will throw an exception error if used incorrectly. According to the docs when using the as operator you're performing an unsafe operation and if they are not of the same type it will throw an error, whereas the force unwrap !! will throw an error only if the variable being unwraped is null.

Tags:

Kotlin