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.