[Null safety] Kotlin Java comparison memo Correct use of Null safety

What article?

How do you write something like this in Java in Kotlin? A memorandum of code comparison.

[Kotlin] [Java] Kotlin Java comparison memo This article specializes in the Null safety function part of.

I want to know the basics of Kotlin by comparing it with Java! Please see the above article for those who say.

Target audience

--Can read and write Java to some extent --I can read Kotlin's basic grammar somehow ――I don't understand the meaning of Kotlin? -I want to know how to get rid of the type with?

What is Null Safety?

It refers to a mechanism that does not allow nulls in principle.

For example, the following code

test.kt


val a: String = null

This will result in a compilation error. That's because Kotlin doesn't allow nulls by default. If you want to allow null, add? After the type.

test.kt


val a: String? = null //This is OK

A Null check is always required when accessing variables defined with options. Let's compare it with Java.

test.java


String a = null;
a.contains("hoge"); //Naturally it is null, so it falls with a nullpo

test.kt


var a: String? = null
a.contains("hoge") //It doesn't compile in the first place

This Kotlin source is played at compile time. This is because non-null is not guaranteed. To access Nullable types:

test.kt


val a: String? = null
a?.contains("hoge")

For a Nullable type function call, just do something like ?. If a is Null, contains will not be executed and Null will be returned.

When I try to achieve this with Java,

First of all, the familiar Null check.

test.java


String a = null;

if (a != null) {
    a.contains("hoge"); //If null, it won't pass here
}

With Java 8, you can use the equivalent function called Optional.

test.java


Optional<String> a = Optional.of(null);
a.ifPresent(notNull -> notNull.contains("hoge")) //contains is executed only when the content of a is not null

You can see that it can be written very concisely compared to Java, Java is just a run-time check, so even if you use Java 8 Optional In the first place, it is not a fundamental solution when you forget to use Optional. Kotlin is checked at compile time, so Null check is not missed.

Return value when calling a Java method

But Kotlin's Null safety features aren't perfect either. For example, when calling a Java method from Kotlin as follows

test.kt


var str = System.getProperty("hoge") //str is String!Mold

Java does not have a null safety feature, so Kotlin will force it to be assigned as a non-null type. If you force the Java value to be non-null, the type will be followed by a!

This is a bit tricky and behaves as non-null, but it could be null.

test.kt


var str = System.getProperty("hoge")
var strLength = str.length //Because it's not a Nullable type?.I don't need

But of course getProperty ("hoge ") can be returned as null, so strLength can drop with a nullpo. This cannot be said to have ensured absolute Null safety.

If you explicitly declare it as Nullable type when receiving the return value of Java method, it will be Nullable type.

test.kt


var str: String? = System.getProperty("hoge")
var strLength = str?.length //Because it is a Nullable type?.Is necessary

It is very difficult to handle this. .. If you want to completely eradicate Null, you need to explicitly make it Nullable.

Nullable unwrap

Let's look at the previous example again

test.kt


var str: String? = System.getProperty("hoge")
var strLength = str?.length

In this case, the type of the variable str is String?, So the strLength to which the value of str length is assigned is ʻInt?`. In other words, if the type of the assignment source is Nullable, the variable of the assignment destination will also be Nullable. With this, only variables that Nullable is premised on are created, and the benefits of the Null safety function cannot be fully received.

If you want to make the type that was once Nullable non-null, there are the following two methods

Forced unwrap

test.kt


var str: String? = System.getProperty("hoge")
var foo = str!!.length // !!You can force str to be of type String by
//foo becomes Int type

If the type that is once Nullable is !!,? Will be removed.

test.kt


var str: String? = System.getProperty("hoge")
str = str!! //str becomes a String type
var foo = str.length // non-Because it's null?.Or!!.No operator needed

Naturally, if str is Null, it will fall with a slimy po. There is no point in making it a Nullable type.

Unwrap using the Elvis operator

test.kt


var str = System.getProperty("hoge") ?: "Unknown Property"
var foo = str.length //str becomes a String type

This ?: Is called the Elvis operator, and evaluates the right expression when the result of the left expression of?: Is Null.

test.java


String str = System.getProperty("hoge") != null ? System.getProperty("hoge") : "Unknown Property";

Synonymous with.

Even if Systrem.getProperty ("hoge") is Null, a dedicated value is returned at that time, so str becomes String type and foo becomes ʻInt type`. The Nullable type is gone and you can handle variables safely.

If you do not want to return a dedicated value, that is, if it is Null, you do not want to continue the subsequent processing.

test.kt


var str = System.getProperty("hoge") ?: return //str becomes a String type

Then, if System.getProperty ("hoge ") is Null, you can exit the processing of that block.

Practical story

test.kt


var str = System.getProperty("hoge") ?: return //str becomes a String type

By unwrapping like this, it no longer falls off with a slimy po, but However, it is not possible to detect an error just by returning and exiting the process without failing. In the first place, this code should assume that System.getProperty ("hoge") is not null.

If you want to detect an unexpected return without generating a nullpo

test.kt


var str = System.getProperty("hoge") ?: run {
   println("Property was not found!!!")
   return
}

It is necessary to make some kind of log spit out like this. The explanation of run is omitted because it is out of this scope.

Practical story 2

Like Android View, it cannot be initialized at the time of instantiation, There are also cases where it has to be initialized within a specific method such as ʻonCreate`.

test.kt


class HogeActivity: AppCompatActivity(){

    private var textView: TextView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.hoge_layout)
        textView = findViewById(R.id.textView) as TextView
    }
}

Since textView cannot be obtained unless it is inflated with onCreate, The initial value must be null. In that case, the type will be Nullable like TextView ?.

There are two approaches to making this TextView non-null type.

Use the lateinit modifier

lateinit is an operator for lazy evaluation of initialization, and compilation can be performed without setting the initial value when defining variables.

test.kt


class HogeActivity: AppCompatActivity(){

    private lateinit var textView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.hoge_layout)
        textView = findViewById(R.id.textView) as TextView
    }
}

It is no longer necessary to set the initial value at the same time as the variable declaration because the? Is removed from the textView type. The value is entered in textView for the first time with findViewById.

However, what you have to be careful about with lateinit is that it can only be used for variables (var). If the getter is called before the setter is called, it will crash.

use by lazy

The explanation of by is a little complicated by this alone, so I will not explain it in detail, A qualifier that allows you to delegate property settings to another class. lazy is a function that is executed the first time you access a property that defines lazy.

By combining the two, you can initialize variables with any function in another class.

test.kt


class HogeActivity: AppCompatActivity(){

    private val textView: TextView by lazy { findViewById(R.id.textView) as TextView }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.hoge_layout)
    }
}

This way, when the textView getter is called for the first time, before the getter findViewById(R.id.textView) as TextView Is executed and the initial value is entered.

By lazy, on the contrary, can only be used for constants (val). Instead, you don't have to worry about crashing like lateinit.

If you can use by lazy, I think it's better to use this. Is the use of lateinit such as the case defined by another class like DI dagger?

at the end

Null safety is a powerful feature, but it's not a silver bullet that solves everything. On the contrary, it can be said that it prevents the spread of defects to other sources because it falls off with a slimy po.

When using the Null safety function, I want to understand the danger behind it before using it.

Recommended Posts

[Null safety] Kotlin Java comparison memo Correct use of Null safety
[Java] Correct comparison of String type
[Java] use Optional instead of if (hoge! = null) {...}
[Java] Speed comparison of string concatenation
Generics of Kotlin for Java developers
Comparison of nullable, non-nullable, non-nullable and safe types from the perspective of null safety
Memo: [Java] Check the contents of the directory
[Java] [Maven3] Summary of how to use Maven3
Directory change monitoring method memo (Java, Kotlin)
[Java] Comparison of String type character strings
Kotlin's Null safety to send to Java developers
Memo for migration from java to kotlin
[Java] Why use StringUtils.isEmpty () instead of String.isEmpty ()
Java memo
Use jenv to enable multiple versions of Java
The story of low-level string comparison in Java
[Java / Kotlin] Resize considering the orientation of the image
Why use setters/getters instead of public/private in Java