Kotlin

【入門】Kotlinのススメ Javaとの比較やメリットを中心に解説します

この記事では、Javaと比較したKotlinのメリットについて記載し、Kotlinがいかに優れた言語なのかを具体例と共に紹介します。

Java経験者の方はKotlinの導入を難しく考えずに、Better Java(より良いJava)くらいの意識で使い始めるのが良いと思います。
JavaからKotlinへは比較的抵抗なく移行することが出来ますし、難しい機能を覚えなくても十分便利な言語だからです。

最近はAndroidだけでなく、サーバーサイドKotlinも事例が増えてきました。
私も実際に5年以上使ってみて、Javaより確実に生産性が上がることを実感しているので、この言語は自信を持ってオススメ出来ます。

Kotlin導入を迷っている、不安だという組織やエンジニアに向けて、Javaと比較して便利になる部分を中心に、以下の章より順に解説します!

Javaと共存可能

KotlinはJavaと100%の互換性があり、クラスを相互に呼び出すことが可能です。
JavaとKotlinのコードを混ぜてコンパイルすることも出来るため、部分的にKotlinを導入し、段階的に移行することも可能です。

使い始める前の安心感という意味でも非常に重要なので、最初に紹介しました。

ゴイチ

いざとなったらJavaに戻ればいい!

関数の定義が fun

Kotlinの関数は fun というキーワードで定義されます。
これには、関数を定義するたびに fun(楽しい)気分になって欲しいという意味も込められているそうです。

ゴイチ

遊び心がありますね!

文字列への埋め込み変数

ポイント

${varibale}の形式で、ダブルクォーテーションで囲まれた文字列に変数を埋め込む事ができる

基本的にこれのためだけにKotlinを導入しても良いんじゃないかというくらい便利で、かつ簡単に使えて、生産性の向上にも寄与する素晴らしい機能です。

Javaで同じことをする場合に比べて、非常に見やすくて、間違いが少ないコードになるので、すぐにKotlinの便利さが分かると思います。

以下の例では、nameという変数を直接文字列の中に埋め込んで表示しています。

fun example(name: String) {
    println("Hello!! $name さん")
}

// 出力結果 (name=ゴイチ)
Hello!! ゴイチ さん

変数が多い場合には特に便利ですし、変数だけじゃなく式も直接埋め込む事ができます。

fun example2(name: String, birthday: LocalDate) {
    println("名前: $name / 生年月日: ${birthday.format(ISO_LOCAL_DATE)} / " +
            "年齢: ${ChronoUnit.YEARS.between(birthday, LocalDate.now())}")
}

// 出力結果 (name=ゴイチ, birthday=1980/1/1)
名前: ゴイチ / 生年月日: 1980-01-01 / 年齢: 42

data class

ポイント

単純なデータオブジェクト(いわゆるDTO)を簡単に定義することができる

data class という特別なクラスがあり、getter / setter / equals / hashCode / toString が既に実装済みのDTOを簡単な記述で定義することができます。

ただ値を持たせるだけのクラスを作る場合に有効です。
JavaのDTO classは、Kotlinでは基本的にdata classになります。

// これがData Class↓
data class User(val name: String, val age: Int)

fun example3(user: User) {
    // JavaのGetterのようなもの
    println("name: ${user.name} / age: ${user.age}")
    // 自動生成されたtoStringメソッド
    println(user.toString())
}

// 出力結果
name: ゴイチ / age: 100
User(name=ゴイチ, age=100)

Javaの方も、Java16で data classとほぼ同じ機能を持つ record が導入されました

listOf / mapOf

ポイント

listOf / mapOf で簡単にコレクションを生成することができる

listOf/ mapOfというキーワードで、よく使うコレクションを生成することができます。
このキーワードは、コードを書いた人がどのような意図を持ってそのコレクションを選んだかが明確になる点が良いと思います。

fun example4() {
    val immutableList = listOf("a", "bb", "ccc")
    val immutableMap = mapOf(1 to "ONE", 2 to "TWO", 3 to "THREE")

    val mutableList = mutableListOf("a", "bb", "ccc")
    val mutableMap = mutableMapOf(1 to "ONE", 2 to "TWO", 3 to "THREE")

    // Mutableの場合のみ、コレクションの変更が可能
    mutableList.add("dddd")
    mutableMap[4] = "FOUR"
}

arrayListOf / linkedMapOfなど実装クラスを指定できるものも用意されています。

when

ポイント

switch-caseより強力な when式が使える

Kotlinには switch-caseの代わりに when式(When expression)が用意されています。

switchの様にbreakを書く必要もないので、コードの可読性が上がります。

式なので、whenの結果を変数に代入したり、関数の戻り値にすることもできます。

fun example5(domain: String) {
    when (domain) {
        "google.co.jp" -> println("Google")
        "yahoo.co.jp", "yahoo.com" -> println("Yahoo")
        "4engineer.net" -> println("プログラミング初心者のためのブログ")
        else -> println("Unknown")
    }
}

fun example6(num: Int): String {
    // whenの後ろにカッコが付かないパターン
    return when { // 式なのでそのままリターンできる
        num < 0 -> "負数"
        num == 0 -> "ゼロ"
        else -> "正数"
    }
}

関数からタプルを返せる

ポイント

関数の戻り値で複数の値を返すことができる

Javaを使っていて、関数からの戻り値が一つしか返せないことが不便に思ったことはないですか?
複数の値を返すために、独自のクラス定義をしたりするケースがあると思います。

Kotlinでは、PairTripleを使って関数から簡単に複数の値を返すことができます。

fun example7() {
    // 返り値で二値、三値を受け取るパターン
    val (num, str) = returnPair()
    val (num2, str2, kanji) = returnTriple()
    println("$num, $str")
    println("$num2, $str2, $kanji")

    // Pair, Tripleとして受け取ることもできる
    val pair = returnPair()
    val triple = returnTriple()
    println("${pair.first}, ${pair.second}")
    println("${triple.first}, ${triple.second}, ${triple.third}")
}

private fun returnPair(): Pair<Int, String> {
    return Pair(1, "ONE")
}

private fun returnTriple(): Triple<Int, String, String> {
    return Triple(1, "ONE", "一")
}

// 出力結果
1, ONE
1, ONE, 一
1, ONE
1, ONE, 一

nullable型

ポイント

Null許容型とNull非許容型が言語的に用意されている

JavaではOptional型を使わないと表せなかった事が、Kotlinでは言語仕様で既に用意されています。

Null非許容型にNullが入る可能性があるコードを書いた場合、コンパイル時に検知してコンパイラが教えてくれます。

Null許容型に用意されている、「エルビス演算子」や「let演算子」なども可読性が良く非常に便利に使えます。

fun example8(hello: String, helloNullable: String?) { // Null許容型は型の後ろに?が付きます
    // Null非許容型の hello は絶対にnullにならないことが保証されるので比較的安全です
    // 基本的にはNull非許容型を優先的に使う方が良い
    println(hello)

    // エルビス演算子
    val helloNotNull = helloNullable ?: "こんにちは" // helloNullableが nullの場合、"こんにちは"が代入される
    println(helloNotNull)

    // let演算子
    helloNullable?.let {
        // helloNullableが nullじゃない場合のみ実行される
        println(it)
    }
}

デフォルトパラメータ

ポイント

関数のデフォルトパラメータが使える

関数に値を渡さない場合にデフォルトの値を使いたい場合があると思います。

Javaではメソッドのオーバーロードを使って複数のメソッドを定義しなければなりませんが、Kotlinでは関数のパラメータのデフォルト値を設定することができます。

しかも、関数の引数の順番を変えて記述することも可能です!

fun example9() {
    // 全てデフォルト値を使うパターン
    defaultParam()
    // 第三引数を省略するパターン
    defaultParam("ゴイチ", 42)
    // 第三引数のみ指定するパターン
    defaultParam(date = LocalDate.of(2022, 12, 31))
    // 記述の順番を変えることもできます
    defaultParam(age = 42, date = LocalDate.of(2022, 12, 31), name = "ゴイチ")
}

private fun defaultParam(name: String = "デフォルト名", age: Int = 20, date: LocalDate = LocalDate.now()) {
    println("$name, $age, $date")
}

// 出力結果
デフォルト名, 20, 2022-04-04
ゴイチ, 42, 2022-04-04
デフォルト名, 20, 2022-12-31
ゴイチ, 42, 2022-12-31

スマートキャスト

ポイント

キャストより便利なスマートキャストが使える

Javaでは型をキャストする場合、instanceof で型チェックした後にキャストを行いますが、Kotlinではスマートキャストといって明示的なキャストを記述しなくても自動的にキャストが行われる仕組みがあります。

if文で型の判定後はキャストなしでオブジェクトを扱えます。(Anyクラスとは、Javaで言う Objectクラスのようなものです)

fun example10(obj: Any) {
    if (obj is String) { // スマートキャスト
        // is で判定後は、obj は String と見なされる
        println(obj)
    }
}

拡張関数

ポイント

拡張関数と呼ばれる、クラスの外部からメソッドを追加する仕組みがある

拡張関数を使えば、ライブラリに含まれるクラスなど、通常ソースコードを変更できないクラスであっても、クラスの外部からメソッドを自由に追加することができます。

例えば、StringDate クラスなどに独自のメソッドを追加したりすることができます。

使い方によっては非常に強力で、ソースコードの可読性を向上させることができます。
ただし、乱用するとソースコードが追いづらくなる面もあるので、必要最低限の利用に留めるのがいいと思います。

fun example11() {
    // 文字列に Prefix と Suffix を付加する拡張関数
    val str = "こんにちは".addPrefixSuffix("<<<", ">>>")
    println(str)

    // Date -> LocalDateTime に変換する拡張関数
    val localDateTime = Date().toLocalDateTime()
    println(localDateTime)
}

// Stringの拡張関数
fun String.addPrefixSuffix(prefix: String, suffix: String): String {
    return "$prefix$this$suffix"
}

// Dateの拡張関数
fun Date.toLocalDateTime(): LocalDateTime {
    return this.toInstant()
        .atZone(ZoneId.systemDefault())
        .toLocalDateTime()
}

// 出力結果
<<<こんにちは>>>
2022-04-04T21:55:23.206

ラムダ式のit

ポイント

ラムダ式の変数名を決めなくても、デフォルト名の it が使える

Javaではラムダ式のパラメータ名を必ず決める必要がありますが、Kotlinではデフォルトの名前が用意されており指定しない場合 it という名称の変数を使うことができます。

特に特別な変数名を付ける程でもない場合に、便利に使うことができます。
勿論、Javaの様に変数名を決めることもできます。

fun example12() {
    val list = IntRange(1, 10) // 1~10の数値
        .map { it * 2 } // 2倍する
        .toList()

    println(list)
}

// 出力結果
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

まとめ

最後に、この記事の内容をまとめます。

要点まとめ

  • Javaと比較してKotlinが優れている点をいくつか挙げた
  • Kotlinの難しい機能を使わなくても Better Java として便利に利用することができる
  • これらの機能を使いながら徐々にKotlinに慣れていけば良い

Androidの世界では半強制的にJavaからKotlinへの移行が成されましたが、Webの世界では強制力が無いためJavaからの移行が中々進まない面があると思います。

まずは簡単な機能を使ってKotlinの便利さに触れ、徐々にJavaから移行していけば、Kotlinの高い生産性の恩恵を受けることができるようになると思います。
バッチ処理などの比較的他への影響が少ないプロジェクトなどから適用してみて、慣れてきたらAPIサーバやWebサイトの開発などにも応用していくようにすれば、比較的導入しやすいのではないかと思います。

ゴイチ

Kotlinは後発なだけあって、シンプルな仕様でとても開発がしやすい言語です

それでは、また他の記事でお会いしましょう!

この記事は役に立ちましたか?

  • この記事を書いた人
アバター画像

ゴイチ

ソフトウェアエンジニア歴20年。 C/C++、Java、C#、Kotlinが得意で、組込系からAndroidアプリ、大規模なWebサービスなど幅広いプログラミング経験があります。 現在は某SNSの会社でWebエンジニアをしています。

-Kotlin
-, ,