🐈‍⬛

Dart、Kotlin、SwiftでのNullの扱い方クイズ

に公開

こんにちは。モバイルエンジニアの高橋です。
主にiOSとFlutterの開発を行っていますが、たまにAndroidの開発も担当しています。
今回は、DartやKotlin、SwiftにおけるNullの扱い方について、知っておかないとバグを生みそうなパターンをクイズ形式で紹介します。これらの言語のnull安全性の違いを理解することで、より安全で意図した通りのコードを書くことができるようになります。

1. Dart

https://dart.dev/language

太郎さんは、int型の値がnullの場合とそうでない場合に、以下のルールに基づいた表示を行いたかったため、次のExtensionメソッドを作成しました。

  • 値がnullの場合は、--を返す
  • 値がnullでない場合は、値をString型で返す
extension IntExt on int? {
  String toStringOrDash() => this?.toString() ?? '--';
}

1-1. Case 1

さて、以下の出力結果はどのようになるでしょうか?

int? value = null;
print("value?.toStringOrDash()個");
出力結果は?

出力結果は「null個」になります。
「--個」の出力を期待していましたが、意図した出力にはなりませんでした。

1-2. Case 2

では、以下の出力結果はどのようになるでしょうか?

int? value = null;
print("value.toStringOrDash()個");
出力結果は?

出力結果は「--個」になります。
今回は、意図した出力結果になりました。

1-3. つまり?

Case 1の書き方では、valueがnullの場合、Extensionメソッドを通ることなくnullのまま出力されてしまいます。これは、以下の動作によるものです。

Null-aware演算子 (?.) の動作:

?.演算子は、左側のオペランドがnullの場合に右側のメソッドやプロパティの呼び出しをスキップし、全体の式の結果をnullにします。

文字列補間の動作:

Dartの文字列補間では、補間式の結果がnullの場合、そのnullが文字列として補間されます。
つまり、"${null}"は"null"という文字列になります。

2. Kotlin

https://kotlinlang.org/docs/home.html

Dartの時と同じExtensionメソッドを作成して、Kotlinの場合も検証してみましょう。

fun Int?.toStringOrDash(): String {
    return this?.toString() ?: "--"
}

2-1. Case 1

さて、以下の出力結果はどのようになるでしょうか?

val value: Int? = null
println("${value?.toStringOrDash()}個")
出力結果は?

出力結果は「null個」になります。
Dartの時と同じく、「--個」の出力を期待していましたが、意図した出力にはなりませんでした。

2-2. Case 2

では、以下の出力結果はどのようになるでしょうか?

val value: Int? = null
println("${value.toStringOrDash()}個")
出力結果は?

出力結果は「--個」になります。
こちらもDartの時と同じく、意図した出力結果になりました。

2-3. つまり?

Kotlinでも、Dartと同様にCase 1のような書き方をすると、valueがnullの場合、Extensionメソッドを通ることなくnullのまま出力されます。

3. Swift

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

Swiftでも、これまでと同じExtensionメソッドで検証してみましょう。

extension Optional where Wrapped == Int {
    func toStringOrDash() -> String {
        return self.map { String($0) } ?? "--"
    }
}

3-1. Case 1

var value: Int? = nil
print("\(value?.toStringOrDash())個");
出力結果は?

出力結果はありません。
こちらはコンパイルエラーになるため、結果を出力することができません。

3-2. Case 2

var value: Int? = nil
print("\(value.toStringOrDash())個");
出力結果は?

出力結果は「--個」になります。
意図した出力結果になりました。

3-3. つまり?

Swiftの場合、Case 1のような書き方をすることはできません。オプショナルチェイニングを使用する場合は、以下のように??演算子でnilの場合の値を定義する必要があります。

print("\(value?.toStringOrDash() ?? "--")個");

オプショナルチェイニングの動作:

Swiftでは、オプショナルチェイニングを使用すると、チェインの途中でnilが発生した場合にその結果がnilになります。これにより、メソッドやプロパティの呼び出しがスキップされます。しかし、Dartとは異なり、オプショナルチェイニングの結果をそのまま文字列として出力することはできません。??演算子を使用して、nilの場合のデフォルト値を提供する必要があります。

文字列補間の動作:

Swiftの文字列補間では、補間式の結果がnilの場合、nilをそのまま文字列として出力することはできません。代わりに、??演算子を使ってデフォルトの文字列を指定することで、nilを回避することができます。この動作により、予期しない出力を防ぐことができます。

4. まとめ

Dart、Kotlin、Swiftは、null安全性をサポートしていますが、その動作にはいくつかの違いがあります。この記事では、それぞれのnullの扱い方と、オプショナルチェイニングの動作について比較しました。

今回は少しイレギュラーなExtensionメソッドで検証を行いましたが、標準メソッド(例えば、DartのtoString()やKotlinのtoString())を扱う際にも、オプショナルチェイニングした際の出力結果には注意が必要です。各言語の仕様を理解し、適切にnullを扱うことで、予期しない動作を防ぐことができます。
各言語仕様の違いを理解することで、Dart、Kotlin、Swiftのコードを書く際に、より安全で意図した通りの動作を実現することができます。どの言語でも、nullの扱いに注意を払い、適切な演算子やメソッドを使用することが重要です。

Money Forward Developers

Discussion