💡

[Kotlin]data classとclassの違い&Javaの何に当たるのか

2024/07/01に公開

data classとclassについて

今回はテーマの通り、

Kotlinのdata classと通常のclassの違いについてと、
Javaにはdata classが存在しなかったのに、なぜKotlinにはdata classがあるのか
(data classはjavaの何に当たるのか)

について書いていきたいと思います!

data classとclassはどう違うのか

最初にそれぞれを一言で言ってしまえば、
通常のクラスは、オブジェクトの状態と振る舞いをカプセル化するために使用され、
データクラスは、主にデータの保持と転送を目的としているもので、Kotlin特有の仕様だ。

classとdata classの主な違いとその特徴について書いていきます。

data class

Data classes in Kotlin are primarily used to hold data. For each data class, the compiler automatically generates additional member functions that allow you to print an instance to readable output, compare instances, copy instances, and more. Data classes are marked with data:
(訳)Kotlinのデータクラスは、主にデータを保持するために使用されます。データクラスごとに、コンパイラはインスタンスを読みやすい形式で出力したり、インスタンスを比較したり、インスタンスをコピーしたりするための追加のメンバ関数を自動的に生成します。データクラスはdataキーワードでマークされます。
---Kotlin Docs: data-classes

  • Kotlin特有の仕様で、dataを持つ(保持する)ことに特化したクラス
     メソッドを持つことはできない.

data classとして成り立たせるには,

  • プライマリコンストラクタに、必ず1つ以上のプロパティを持っていなければいけない。
    • また、 val または var としてマークされなければならない。
          (privateprotectedは使えない。)
  • data キーワード: クラス宣言の前にdataキーワードを付ける必要がある。
  • 修飾子: データクラスはabstract、open、sealed、innerのいずれかであってはいけない
data class User(val name: String, val age: Int)
  • Kotlinはデータクラスに対していくつかの便利な機能を自動生成する。
    toString(), equals(), hashCode(), copy(), componentN()
    (Nはプロパティの位置)といったメソッドを自動的に生成。
    = ここにdata classにすることのメリットがある!

わざわざdata classとして定義するメリットは何?

わざわざdata classとして定義するメリットは何なのか。
それは、データクラスに対して、
上記したようにいくつかの便利な機能を自動生成していることにある。
これらをちょっと詳しくする。

■ Anyクラスの継承。(=すべてのクラスのスーパークラス)
Anyクラスでは3つの関数が実装されていて全て使用可能。

data classでのみ使用可能なメソッド

data classとclassの違い: 関数の仕様の違い

No 関数名称 データクラスの実装 通常クラスの実装
1 equals() プロパティ値を比較する
※プライマリコンストラクタで
初期化した値しか材料として使用しない。
インスタンスを比較する
2 hashCode() プロパティ値からハッシュコードを生成 ユニークなハッシュコードを生成
3 toString() "User(name=John, age=42)"形式の文字列を返す "型名@ハッシュ値(16進数)"形式の文字列を返す
4 componentN() プロパティにcomponent1, component2でアクセスできる 実装なし
5 copy() インスタンスをコピーする.
オブジェクトをコピーして
一部のプロパティを
変更しながら、他のプロパティは
そのままにすることができる
実装なし

なぜJavaにはないdata classがKotlinにあるのか

kotlinのdata classはJavaの何に相当するのか

Kotlinのデータクラスは、Javaでは以下に相当する。

  • Lombokライブラリ@Dataアノテーションを使用したクラス
  • Plain Old Java Object(POJO)

Javaでは、データを保持するためのクラスを作成する際に、
多くのボイラープレートコードが必要だ。
具体的には以下のようなものがある。
・メンバー変数にアクセスするための getter / setter メソッド
・equals/hashCodeメソッド
・toStringメソッド
・コンストラクタ
・リソース(入出力ストリーム等)のクローズ処理
・ロガーインスタンスの生成

※ボイラープレートコード:
殆ど、または全く変化することなく、複数の場所で繰り返される定型コードのセクションのこと

Lombokを使用することで、これらのメソッドを自動生成することができるが、
外部ライブラリによるサポートだ。

コードで見てみる。

POJOでかく

※POJO: Plain Old Java Object (POJO)
Javaで記述されるオブジェクトのうち、特定のフレームワークやオブジェクトモデル、
規約などに縛られず、また、これらに依存しないシンプルな仕様のもの。
「昔からある単なるJavaオブジェクト」の意。

ex, POJOでかく
public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


Lombok

LombokはJavaプログラムにおけるボイラープレートコード(冗長な定型コード)を
削減するためのライブラリ。
Lombokを使用することで、アノテーションを追加するだけで、
さまざまなメソッドや機能を自動生成することができボイラープレートコードを大幅に削減できる。

lombok

lombokの@Dataアノテーションをつけることで以下をまとめて適用するため大幅に削減できる。

  • @ToString, @EqualsAndHashCode,
  • @Getter, @Setter, @RequiredArgsConstructorを含む。
  • @Builder: ビルダーパターンのコードを自動生成します。
  • @NoArgsConstructor, @AllArgsConstructor, @RequiredArgsConstructor: コンストラクタを自動生成。
import lombok.Data;

@Data
public class User {
    private final String name;
    private final int age;
}

Kotlinで上記を書くと...

一方、Kotlinのデータクラスは、これらの機能を言語仕様として標準で提供している。
上記をKotlinで書くとこうなる。

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

data classのデフォルト値について

On the JVM, if the generated class needs to have a parameterless constructor, default values for the properties have to be specified (see Constructors):

JVM上で動作する際に生成されるクラスがパラメータなしのコンストラクタを持つ必要がある場合、
プロパティにデフォルト値を指定する必要がある。デフォルト値を指定することで、
パラメータなしのコンストラクタが自動的に生成される。

data class User(val name: String = "", val age: Int = 0)

別の言い方したら、
Kotlinのデータクラスにおいてプロパティにデフォルト値を指定することで、
デフォルトコンストラクタ(パラメータなしのコンストラクタ)が自動的に生成されるので
オブジェクトを生成する際に全てのプロパティに対して明示的に値を渡す必要がなくなる

ということ!

Ex. デフォルト設定なし

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

fun main() {
    val user = User("Alice", 25) // OK
    val userWithoutName = User() // エラー

    println(user) // OK
    println(userWithoutName) // エラー: 必要なパラメータ 'name' が不足しています
}

ex. デフォルト設定あり

data class User(val name: String="Airi", val age: Int=100)

fun main() {
    val user = User() 
    println(user)
}

Javaの何を解決したくてできたのか

Kotlinのデータクラスは、Javaのボイラープレートコードの多さ、コードの冗長性、
実装の一貫性の欠如などの問題を解決するために設計された1つだ。
データクラスを使用することで、開発者はより簡潔で可読性の高いコードを記述できるようになる。


Discussion