Kotlin's type system is a type that can be null,
Distinguish between types that cannot be.
For types that can be null, add ?
After the type name.
val num: Int = null //Compile error cannot be null
val num: Int? = null //OK if nullable type
You cannot directly access nullable functions and properties. A compile error will occur.
val num: Int? = null //Declared as nullable type
val floatNum = num.toFloat() //Compile error
Inaccessible if it may be null means The risk of NullPointerException is greatly reduced.
You'll often see such null checks in Java.
// Java
Dog dog = //May be null
dog.run(); //Possible NullPointerException Ali
if (dog != null) {
dog.run(); //Null checked
}
Let's implement the same in Kotlin.
val dog: Dog? = //May be null
dog.run() //Compile error
if (dog != null) {
dog.run() //OK for null-checked blocks
}
Functions for nullable objects cannot be called directly. Automatically cast to null-free type in null-checked if blocks You can call the function. This is called a smart cast.
Kotlin also has more convenient syntactic sugar.
val dog: Dog? = //May be null
dog?.run()
You can access function calls and properties after ?.
.
In the above example, if dog
is null, it just returns null.
This is called a safe call.
You'll see downcast code like this in Java.
/*
*Dog class is a subclass of Animal class
*/
Animal animal = // ...
((Dog)animal).run(); //Possibility of ClassCastException Ali
if (animal instanceof Dog) {
Dog dog = (Dog)animal;
dog.run();
}
Let's implement the same in Kotlin.
/*
*Dog class is a subclass of Animal class
*/
val animal: Animal = // ...
(animal as Dog).run() // //Possibility of ClassCastException Ali
if (animal is Dog) {
animal.run()
}
Kotlin casts use the ʻas` operator. If the cast fails, a ClassCastException will be thrown.
The Java equivalent of ʻinstanceof is the ʻis
operator.
This is also by smart cast
In the if block, the variable ʻanimal` is automatically cast to Dog type,
You can call the method of Dog class as it is.
Kotlin also has more convenient syntactic sugar.
val animal: Animal = // ...
(animal as? Dog)?.run()
The ʻas? Operator casts it to the
Dog? `Type in the above example.
If you can't cast, it will return null, which is safe.
Besides the safe call (with .?
), there is a way to handle nullable types.
val num: Int? = //May be null
val floatNum = num.toFloat() //Compile error
val floatNum = num!!.toFloat() //Compile OK but possible NullPointerException
Add . !!
.
There are some situations where it is necessary, but it should not be used as much as possible.
You will often see this kind of branching in Java.
// Java
Dog dog = getDog(); //The return value of getDog may be null
if (dog == null) {
dog = new Dog();
}
dog.run();
kotlin has a handy operator for "if null".
val dog = getDog() ?: Dog()
dog.run()
?:
Elvis operator
Nullable variables on the left
On the right side, describe the process to be executed if it is null.
It's common to create an alternate object or throw an Exception.
When declaring a class property, you may have to make it a nullable type. Framework classes that cannot override constructors, Properties that can only be initialized after a certain callback ...
However, they are usually cumbersome to work with nullable types, and you may want to treat them as virtually non-nullable types. (Often actually developed in Kotlin)
In such cases, the lateinit
modifier is useful.
//View class for login
class LoginView {
//It has child elements such as TextView and Button,
//These are generated from resources...Virtual UI system
lateinit var emailTextView: TextView
lateinit var passwordTextView: TextView
lateinit var loginButton: Button
//Child elements can be retrieved when View is loaded from a resource
fun onLoadViewFromResource(viewResource: View) {
this.emailTextView = viewResource.children[0]
this.passwordTextView = viewResource.children[1]
this.loginButton = viewResource.children[2]
}
}
The lateinit
modifier can delay initialization,
Note that accessing the property before initialization will result in an Exception.
In addition to this, there is a property delegate called lazy
.
This is useful for deferred initialization of properties that are expensive to initialize.
class LoginView {
val loginConnection: LoginConnection by lazy {
LoginConnection() //Login Connection is expensive to generate
}
}
Recommended Posts