Kotlinデータクラスの使い方!完全ガイドのたった15選 – Japanシーモア

Kotlinデータクラスの使い方!完全ガイドのたった15選

Kotlin言語のロゴとデータクラスのイメージ図Kotlin
この記事は約16分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

このサービスは複数のSSPによる協力の下、運営されています。

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

この記事を読めばKotlinのデータクラスを効果的に使うことができるようになります。

初めてKotlinを学ぶ方から、データクラスの利用方法を深めたいと考えている方まで、幅広く対応しています。

Kotlinは現代のアプリケーション開発において欠かせない言語となっており、特にAndroidアプリケーションの開発においては、Kotlinが主流となっています。

その中でも、データクラスはKotlinの魅力的な特徴の一つと言えるでしょう。

では、具体的にKotlinのデータクラスとは何か、どのように利用するのかを、詳しく見ていきましょう。

●Kotlinのデータクラスとは

Kotlinのデータクラスは、データの保持を主目的としたクラスです。

JavaのPOJO(Plain Old Java Object)のようなものと考えると分かりやすいでしょう。

しかし、Kotlinのデータクラスは、POJOよりも多くの機能を持っており、コードの冗長性を大幅に減少させることができます。

○データクラスの基本

データクラスを定義する際には、dataキーワードを使用します。

このdataキーワードをクラスの宣言前につけるだけで、そのクラスはデータクラスとして振る舞います。

データクラスは主に次の特徴を持っています。

  1. equals()hashCode()toString()のメソッドが自動的に生成されます。
  2. copy()メソッドが自動的に提供され、オブジェクトの複製が簡単にできます。
  3. コンポーネント関数componentN()が自動的に生成され、分解宣言を使用することができます。

これらの自動生成されるメソッドや関数は、実際の開発において非常に便利で、手動でこれらのメソッドを書く必要がなくなります。

これにより、Kotlinでのプログラミングがよりシンプルで効率的になります。

●データクラスの使い方

Kotlinのデータクラスの使い方を知ることで、効率的なプログラムを書くことが可能になります。

特に、データの取り扱いが多い場面でのコードの簡潔性や可読性の向上が期待できます。

○サンプルコード1:基本的なデータクラスの定義

Kotlinでのデータクラスの基本的な定義方法を次のサンプルコードで確認しましょう。

data class Person(val name: String, val age: Int)

このコードではPersonという名前のデータクラスを定義しています。

nameageというプロパティを持ち、それぞれ文字列型と整数型の値を保持することができます。

このように、データクラスは非常にシンプルに定義できます。

このコードを実行すると、Personというデータクラスが定義され、後続の処理でこのクラスをインスタンス化して利用することができます。

○サンプルコード2:copyメソッドの利用

データクラスはcopyメソッドを持っており、これを利用することでオブジェクトのコピーを簡単に作成することができます。

下記のサンプルコードでは、既存のPersonオブジェクトから新しいPersonオブジェクトを生成しています。

val person1 = Person("Taro", 25)
val person2 = person1.copy(age = 26)

このコードでは、まずperson1というPersonオブジェクトを生成しています。

次に、copyメソッドを利用して、person1ageプロパティだけを変更して新しいオブジェクトperson2を生成しています。

このコードを実行すると、person2nameが”Taro”、ageが26というプロパティの値を持つ新しいPersonオブジェクトとして生成されます。

○サンプルコード3:component関数の利用

データクラスで定義したオブジェクトのプロパティを取得する際、componentN関数が自動的に提供されます。

ここでのNはプロパティの位置を示す番号になります。これにより、複数のプロパティを簡単に分解して取得することが可能です。

例として、次のPersonデータクラスを考えてみましょう。

data class Person(val name: String, val age: Int)

このデータクラスでは、nameプロパティを取得するためのcomponent1関数と、ageプロパティを取得するためのcomponent2関数が自動生成されます。

サンプルコードを参考に、実際にcomponent関数を使ったプロパティの取得方法を確認します。

val person = Person("Taro", 25)
val (name, age) = person
println("名前: $name, 年齢: $age")

このコードでは、データクラスPersonから生成されたオブジェクトpersonnameageプロパティを、それぞれ変数nameageに代入しています。

そして、変数を使用してその値を出力しています。

このコードを実行すると、”名前: Taro, 年齢: 25″という結果が出力されます。

このように、component関数を利用することで、データクラスのプロパティを簡単に取得・使用することができます。

○サンプルコード4:equals()とhashCode()の自動生成

Kotlinのデータクラスは、equals()hashCode()のメソッドも自動的にオーバーライドされます。

これにより、オブジェクト同士の比較やハッシュコードの取得が効率的に行われます。

例として、次のコードを見てみましょう。

val person1 = Person("Taro", 25)
val person2 = Person("Taro", 25)
println(person1 == person2)

このコードでは、二つの異なるPersonオブジェクトperson1person2を定義していますが、その内容は同じです。

equals()メソッドが自動生成されるため、これらのオブジェクトが内容的に等しいかどうかを==演算子を用いて比較することができます。

このコードを実行すると、trueという結果が出力されます。

これにより、データクラスを使用することでオブジェクトの比較が簡単に行えることがわかります。

○サンプルコード5:toString()メソッドの自動生成

データクラスは、toString()メソッドも自動的にオーバーライドされます。

これにより、オブジェクトを文字列として表現する際にその内容が簡潔に出力されるようになります。

下記のサンプルコードでその動作を確認しましょう。

val person = Person("Taro", 25)
println(person)

このコードを実行すると、”Person(name=Taro, age=25)”という結果が出力されます。

データクラスのインスタンスを直接出力した際、その内容が綺麗にフォーマットされて表示されることがわかります。

●データクラスの応用例

Kotlinのデータクラスは、単なるデータの保持だけでなく、多様なシチュエーションでの活用が可能です。

これから、いくつかの具体的な応用例とその実装方法について深掘りしていきます。

○サンプルコード6:複数のプロパティを持つデータクラス

データクラスは複数のプロパティを持つことが可能です。

たとえば、ユーザーの情報を持つデータクラスを考えてみましょう。

data class User(val id: Int, val name: String, val email: String, val address: String)

このデータクラスを使用して、ユーザーの情報を表現することができます。

ユーザーの情報が変更されることなく、読み取り専用でデータを管理する場面で役立ちます。

このコードでは、ID、名前、メールアドレス、住所の4つのプロパティを持つUserクラスを定義しています。

○サンプルコード7:継承を利用したデータクラス

Kotlinのデータクラスは他のクラスを継承することはできませんが、インターフェースの実装は可能です。

ここでは、Contactというインターフェースを実装したデータクラスのサンプルを紹介します。

interface Contact {
    fun sendMail()
}

data class EmailUser(val email: String) : Contact {
    override fun sendMail() {
        // メール送信のロジック
        println("$email にメールを送信しました。")
    }
}

このコードでは、ContactインターフェースにはsendMailメソッドが定義されており、EmailUserデータクラスはこのインターフェースを実装しています。

これにより、EmailUserクラスのオブジェクトでsendMailメソッドを呼び出すことで、メールを送信するロジックを実行することができます。

具体的には、EmailUserのインスタンスを生成し、sendMailメソッドを呼び出すことで、「[メールアドレス] にメールを送信しました。」というメッセージを出力することができます。

○サンプルコード8:拡張関数とデータクラス

KotlinはJavaよりも表現力が豊かで、様々なプログラミングテクニックが使える言語として知られています。

その中でも、拡張関数はKotlinの特徴的な機能の1つです。拡張関数を使うと、既存のクラスに新しい関数を追加することができます。

これをデータクラスと組み合わせることで、データクラスに独自の操作を追加することが可能になります。

例として、先ほど作成したUserデータクラスに、ユーザーのフルネームを返す拡張関数を追加してみましょう。

data class User(val firstName: String, val lastName: String, val email: String)

// Userデータクラスの拡張関数
fun User.getFullName(): String {
    return "$firstName $lastName"
}

fun main() {
    val user = User("太郎", "山田", "taro.yamada@example.com")
    println(user.getFullName()) // 出力: 太郎 山田
}

このコードでは、UserデータクラスにgetFullNameという拡張関数を追加しています。

この関数は、firstNamelastNameを結合してユーザーのフルネームを返します。

main関数内でこの拡張関数を呼び出すと、”太郎 山田”という結果が得られます。

○サンプルコード9:データクラスとリスト

データクラスをリストとして扱う場面も多いです。

例えば、データベースから複数のユーザー情報を取得して、それをリストとして保持するケースが考えられます。

data class User(val id: Int, val name: String)

fun main() {
    val userList = listOf(
        User(1, "田中"),
        User(2, "佐藤"),
        User(3, "鈴木")
    )

    for (user in userList) {
        println("${user.id}: ${user.name}")
    }
    // 出力:
    // 1: 田中
    // 2: 佐藤
    // 3: 鈴木
}

このサンプルコードでは、Userデータクラスのインスタンスを3つ作成し、それをuserListというリストに格納しています。

for文を使用して、リスト内の各ユーザーのIDと名前を順番に出力しています。

○サンプルコード10:データクラスとマップ変換

データクラスは、マップとしての変換もサポートしています。

これにより、データクラスのインスタンスをキーと値のペアの集合に変換することができます。

data class User(val id: Int, val name: String)

fun main() {
    val user = User(1, "田中")
    val userMap = user.toMap()

    for ((key, value) in userMap) {
        println("$key: $value")
    }
    // 出力:
    // id: 1
    // name: 田中
}

このコードでは、Userデータクラスのインスタンスをマップに変換して、キーと値のペアとして出力しています。

toMap関数を使用すると、データクラスのプロパティ名をキーとして、その値をマップの値として取得することができます。

●注意点と対処法

Kotlinでデータクラスを利用する際には、多くのメリットが享受できますが、その一方でいくつかの注意点も存在します。

これらの注意点を理解し、適切に対処することで、データクラスをより効果的に利用することができます。

○サンプルコード11:varとvalの違い

Kotlinにおけるvarvalの違いは基本的な知識の一つですが、データクラスを定義する際に特に重要となります。

具体的には、varは可変のプロパティを示し、valは不変のプロパティを示します。

data class SampleVar(val id: Int, var name: String)

fun main() {
    val sample = SampleVar(1, "初期名")
    println(sample.name)  // 出力: 初期名

    sample.name = "変更後の名前"
    println(sample.name)  // 出力: 変更後の名前
}

このコードでは、SampleVarデータクラスのnameプロパティがvarで定義されているため、後からその値を変更することが可能です。

一方で、idプロパティはvalで定義されているため、初期化後にその値を変更することはできません。

○サンプルコード12:非データクラスとの比較

データクラスは特定の目的(データの保持)に特化したクラスであり、通常のクラス(非データクラス)とはいくつかの点で動作が異なります。

例として、equalshashCodeの動作を見てみましょう。

class RegularClass(val id: Int, val name: String)

data class DataClass(val id: Int, val name: String)

fun main() {
    val regular1 = RegularClass(1, "A")
    val regular2 = RegularClass(1, "A")
    println(regular1 == regular2)  // 出力: false

    val data1 = DataClass(1, "A")
    val data2 = DataClass(1, "A")
    println(data1 == data2)  // 出力: true
}

このコードでは、非データクラスRegularClassとデータクラスDataClassを定義しています。

RegularClassのインスタンスを2つ作成しても、それらは異なるオブジェクトとして扱われます。

一方、DataClassの場合、プロパティの値が同じであれば、equalsメソッドはtrueを返します。

●カスタマイズ方法

Kotlinのデータクラスは、自動で多くのメソッドを生成してくれる強力な機能を持っています。

しかし、場合によってはデフォルトの動作だけでは要件を満たせないことがあります。

そこで、データクラスのカスタマイズ方法について解説します。

○サンプルコード13:カスタムequals()とhashCode()

データクラスはプロパティの値に基づいてequals()hashCode()を自動生成しますが、特定の条件下でこれらのメソッドをカスタマイズしたい場合もあるでしょう。

data class Person(val id: Int, val name: String) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Person

        if (id != other.id) return false

        return true
    }

    override fun hashCode(): Int {
        return id.hashCode()
    }
}

fun main() {
    val person1 = Person(1, "太郎")
    val person2 = Person(1, "二郎")
    println(person1 == person2)  // 出力: true
}

このコードでは、Personクラスにおいてequals()hashCode()をカスタマイズしています。

ここでは、名前が異なっていてもIDが同じ場合は同一のPersonとみなすようにしています。

○サンプルコード14:カスタムtoString()メソッド

toString()もデータクラスで自動生成されるメソッドの一つですが、出力形式を変更したい場合はオーバーライドしてカスタマイズできます。

data class Animal(val type: String, val age: Int) {
    override fun toString(): String {
        return "動物の種類: $type, 年齢: $age"
    }
}

fun main() {
    val cat = Animal("猫", 3)
    println(cat)  // 出力: 動物の種類: 猫, 年齢: 3
}

このコードでは、AnimalクラスのtoString()をカスタマイズして、出力内容を日本語でわかりやすく表示しています。

○サンプルコード15:データクラスのシールドクラス活用

シールドクラスは、特定の数のサブクラスのみを持つことができるクラスです。

データクラスと組み合わせることで、バリアントを持つデータ構造の定義が可能になります。

sealed class Shape {
    data class Circle(val radius: Double) : Shape()
    data class Rectangle(val width: Double, val height: Double) : Shape()
}

fun getArea(shape: Shape): Double {
    return when(shape) {
        is Shape.Circle -> Math.PI * shape.radius * shape.radius
        is Shape.Rectangle -> shape.width * shape.height
    }
}

fun main() {
    val circle = Shape.Circle(5.0)
    println(getArea(circle))  // 出力: 円の面積を計算した値

    val rectangle = Shape.Rectangle(4.0, 5.0)
    println(getArea(rectangle))  // 出力: 長方形の面積を計算した値
}

上記のコードでは、Shapeというシールドクラスを定義し、その中にCircleRectangleという2つのデータクラスをサブクラスとして持っています。

そして、getArea関数を使用して、各形状の面積を計算しています。

まとめ

Kotlinのデータクラスは、開発者にとって非常に有益な機能を提供します。

自動でメソッドを生成してくれることから、コードの記述量を大幅に削減することができ、また、それによりヒューマンエラーも減少させることが可能です。

今回学んだ内容を基に、Kotlinでのプログラミングスキルを一段と向上させ、より質の高いアプリケーションの開発を目指しましょう。

Kotlinにはまだまだ多くの便利な機能がありますので、引き続き学習を深めていくことをおすすめします。