Null-safety. General information. Symbol ?. Operators ?. and ?:
Contents
- 1. The concept of a null value. Elements that get null-value
- 2. Declaration of variables that support null-value (nullable-variables). Symbol ?
- 3. Ensuring null safety in the Kotlin language
- 4. The safe call operator ?. . Example
- 5. Default null assignment operator ?: (Elvis)
- 6. Check for null. Operator !!
- 7. Function let
- Related topics
Search other resources:
1. The concept of a null value. Elements that get null-value
In modern programming languages (including Java, C#, etc.) there are situations when a value does not contain any value. The terms null-value and zero-value are different things.
In the Kotlin language, null means that there is no value for variables declared as val or var at the moment. Based on this context, Kotlin language elements are divided into two types:
- nullable are elements that can take a null value;
- non-nullable – elements that cannot take a null value.
When writing programs, it is important to use nullable elements correctly to avoid errors. In other programming languages (Java in particular), incorrect use of nullable elements leads to run-time errors, which is very undesirable. As a consequence, an exception is thrown (in Java it is a java.lang.NullPointerException), which is a consequence of the fact that the compiler was unable to detect an error during the compilation of the program.
In the Kotlin language, the type system is organized in such a way that errors using nullable types are already detected at compile time. That is, the error can be immediately identified and corrected, otherwise the program code will not be compiled. This is a significant advantage of the Kotlin language.
⇑
2. Declaration of variables that support null-value (nullable-variables). Symbol ?
Unlike the Java language (as well as other languages), Kotlin allows the programmer to directly specify nullable types. This is done by adding the symbol ? (question mark) to type name
Type?
where
- Type – any basic type of the Kotlin language (Int, Double, Byte, Short, UByte, Char, String and others).
If a variable is declared as a nullable variable, then it can be assigned the null value.
Example.
The example demonstrates the declaration and use of nullable variables of different types.
fun main(args: Array<String>) { var i1 : Int? = null // nullable-variable of Int type val d1 : Double? = null // nullable-variable of Double type val s1 : String? = null // nullable-variable of String type var b1 : Boolean? = null // nullable-variable of Boolean type var c1 : Char? = null // nullable-variable of Char type println(i1) println(d1) println(s1) println(b1) // nullable variables declared as var can be assigned other values i1 = 25; println(i1); b1 = true; println(b1); c1 = '+'; println(c1); }
Result
null null 25 true +
⇑
3. Ensuring null safety in the Kotlin language
If the program declares a type value with null support, then at the compilation stage, a check for null-valid values will be performed. Thus, the range of possible operations will be narrowed. If the operation is not null-safe, an error message will be displayed. Below are possible cases of such errors and attempts to eliminate them.
⇑
3.1. Return the result from function, which may be null
For example, the following code will generate a compilation error:
// A function that returns Int from null-string fun GetLenString(str : String?) : Int { return str.length // error }
If you try to compile this function, you will get an error
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
Indeed, how can you return the length of a string of type String?, which can be null, that is, not exist.
To fix the situation, you need to enter a null-checking code like
// A function that returns an Int from a null string fun GetLenString(str : String?) : Int { if (str!=null) // check for null return str.length else return 0 }
⇑
3.2. Assigning a nullable value to a variable that does not support null
If a variable is declared that does not support a null value, then an attempt to assign the value of a nullable variable to this variable will cause a compilation error.
For example, in the following code
fun main(args: Array<String>) { // 1. Variable supporting null var X : Double? = 23.8 // 2. Variable not supporting null var Y : Double = 33.1 // 3. Assignment attempt Y = X // error }
when trying to compile in the assignment string
Y = X
the compiler will throw an error
Type mismatch: inferred type is Double? but Double was expected
To prevent the compiler from throwing an error, you need to implement a check for null, for example, yes
fun main(args: Array<String>) { // 1. Variable supporting null var X : Double? = 23.8 // 2. Variable not supporting null var Y : Double = 33.1 // 3. Assignment attempt, check for null if (X!=null) Y = X // it works, no error else Y = 0.0 }
⇑
3.3. Passing a null parameter to a function that declares a non-nullable parameter
If a function is declared that takes a non-nullable parameter, then an attempt to pass a nullable value to that function will fail.
For example, in the code below
// A function that receives a value that does not support null fun ShowInteger(num : Int) : Unit { println("num = " + num) } fun main(args: Array<String>) { var num1:Int = 10 // non-nullable type var num2:Int? = 20 // nullable-type ShowInteger(num1); // it works ShowInteger(num2) // here is an error }
compiler in the line
ShowInteger(num2)
will generate an error
Type mismatch: inferred type is Int? but Int was expected
To fix the error, the ShowInteger() function must receive the nullable type Int?. As a result, the function code might look something like this
// A function that receives a value that does not support null fun ShowInteger(num : Int?) : Unit { // Checking for null if (num!=null) println("num = " + num) }
⇑
4. The safe call operator ?.. Example
With a secure call operator ?. you can implement a check for null and a call method. In the most general case, the use of the operator ?. is as follows
obj?.MethodName()
here
- obj – the variable name (object);
- MethodName() – the name of the function (method) that is called from the instance (object) obj. Calling MethodName() can be with passing parameters to it.
The obj?.MethodName() call code is converted by the compiler into code that contains a null check
if (obj!=null) obj.MethodName() else null
Figure 1 shows the operation of the ?. operator.
Figure 1. Operator ?.
Example.
The example uses a modified GetLengthString() function that uses the ?. operator.
// A function that returns a nullable String? value fun GetLengthString(s : String?) : Int? { return s?.length // operator ?. } fun main(args: Array<String>) { // Using the ?. operator to get the length of a null string var s : String? = "abcde" println(GetLengthString(s)) s = null println(GetLengthString(s)) }
Result
5 null
⇑
5. Default null assignment operator ?: (Elvis)
The ?: operator implements a null check based on two operands. This operator is also called the null coalescing operator. In the most general case, the use of the ?: operator is of the form
op1 ?: op2
here op1, op2 are some operands involved in obtaining the result. The result of the operation is formed based on Figure 2.
Figure 2. Operator ?:
Example.
fun main(args: Array<String>) { // Using operator ?: // 1. For String type var s1 : String? = "abcd" var s2 : String = s1 ?: "" // s2 = "abcd" println(s2) s1 = null s2 = s1 ?: "null value" // s2 = "null value" println(s2) // 2. For Int type var i1 : Int? = 25 var i2 : Int = i1 ?: 0 // i2 = 25 println(i2) i1 = null i2 = i1 ?: 0 // i2 = 0 println(i2) }
Result
abcd null value 25 0
⇑
6. Check for null. Operator !!
With the operator !! you can cast to a type that does not support null. If the value itself is null, then Kotlin will throw an exception. Using the !! operator looks like this
obj!!
here obj – some variable (object).
Operator !! work shown in Figure 3.
Figure 3. Operator !!
Example 1. An exception is thrown for type String.
fun main(args: Array<String>) { // Operator !! // Using for String type var s1 : String? = "abcd" var s2 : String = s1!! // s2 = "abcd" println(s2) s1 = null s2 = s1!! // exception NullPointerException }
Result
abcd Exception in thread "main" kotlin.KotlinNullPointerException at TrainKt.main(train.kt:16)
Example 2. Throws an exception for the Int type.
fun main(args: Array<String>) { // Operator !! // Using for Int type var i1 : Int? = 25 var i2 : Int i2 = i1!! // i2 = 25 println(i2) i1 = null i2 = i1!! // The NullPointerException is generated println(i2) }
Result
25 Exception in thread "main" kotlin.KotlinNullPointerException at TrainKt.main(train.kt:13)
⇑
7. Function let
The let function is used for nullable lambda expressions. This function is combined with the ?. operator used to conveniently write nullable expressions.
The general form of using the function is as follows
obj2 = obj1?.let { // code of lambda expression // using it // ... }
here
- obj1, obj2 – variables (objects) of nullable-type.
Figure 4 shows how the let function works.
Figure 4. Function let. Safe invocation of a lambda expression
As you can see from the figure, if obj!=null, then the lambda expression is executed. If obj==null, then nothing happens and the lambda expression is not executed. As the size of the evaluated expression increases, the effect of using the let function increases significantly.
Example.
fun main(args: Array<String>) { // The let function in combination with ?. // 1. For String type // 1.1. nullable-variables are declared var s1 : String? var s2 : String? // 1.2. Not null-value s1 = "abcd" s2 = s1?.let { // Lambda-expression it.reversed() // String reversing } println(s2) // "dcba" // 1.3. null-value s1 = null s2 = s1?.let { it.reversed() } println(s2) // null // 2. Using with Double-type // 2.1. Declare two nullable-variables var d1 : Double? var d2 : Double? // 2.2. Not null-value d1 = 2.33 d2 = d1?.let { // multiply by 3 it * 3 } println(d2) // 6.99 // 2.3. null-value d1 = null d2 = d1?.let { it * 3 } println(d2) // null }
Result
dcba null 6.99 null
⇑
Related topics
⇑