覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Kotlin - クラスとプロパティ

「Kotlinイン・アクション」 を読みながらの写経続いております。(Javaと比較しながら)

Kotlinイン・アクション

Kotlinイン・アクション

  • 作者: Dmitry Jemerov,Svetlana Isakova,長澤太郎,藤原聖,山本純平,yy_yank
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2017/10/31
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (2件) を見る


値クラス

以下はJavaでのクラス宣言の例です。

public final class Person {
    private final String name;

    private final int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

nameage のfinalなフィールドとそのフィールドに対するgetterのみを持つ単純なクラスとなっています。

このクラスを、 Kotlinへコンバートすると以下のようになります。

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

うん、とてもシンプル。

Javaのコードでは、コンストラクタ内で受け取った引数をフィールドに割り当てるコードを繰り返します。
まさにJavaの典型的なボイラープレートコードの一つと言えますが、
Kotlinではこれが無くなっており、すっきりしています。

このようなデータのみが存在して、実装コードが無いクラスは値オブジェクト(value object)と呼ばれる事があります。
多くのプログラミング言語では、これを宣言するための簡潔な構文を用意しています。

残念ながら Java ではこのような構文は提供されておらず、Lombok を利用する事で似たようなことを実現することはできます。


また、Kotlin ではデフォルトの可視性が public であるため、public の記述が無くなっています。


プロパティ

Java では、データを通常はprivateなフィールドに格納し、利用者がそのデータにアクセスする必要があるのであればアクセサメソッド(getterやsetter)を提供します。
このフィールドとアクセサの組み合わせはプロパティ(property)と呼ばれ、多くのライブラリやフレームワークに多用されています。

Kotlin では、Javaとは違い プロパティは第一級の言語機能となっています。これが、Javaにおけるフィールドとアクセサメソッドを置き換えるものとなっています。

Kotlin のクラス内ではプロパティを宣言する方法は、変数を宣言する方法と同じで valvar キーワードを使用します。各々の違いは以下の通りです。

  • val : 宣言されたプロパティは読み取り専用
  • var : 宣言されたプロパティはミュータブルで変更が可能

例えば以下のようなクラス宣言が可能です

class Person(
    val name: String,   // <- 読み取り専用。フィールドとgetter が生成される
    var age: Int        // <- 書き込み可能。フィールドとgetter, setter が生成される
)

プロパティを宣言するときには、それに対応するアクセサ(読み取り専用のプロパティではgetter, 書き込み可能なプロパティではgetter とsetterの両方)も宣言されたことになります。

上記の Person クラスは Java からも Kotlin からも同様に利用する事が可能です。

Java からは以下のように利用可能です。

public static void main(String[] args) {
        Person p = new Person("Taro", 20);

        System.out.println(p.getName());  // -> Taro
        System.out.println(p.getAge());   // -> 20

        p.setAge(25);                     // -> age の値の変更
        System.out.println(p.getAge());   // -> 25
    }

Koitlin からは以下のように利用します

fun main() {
    val p = Person("Taro", 20)

    println(p.name)  // -> Taro
    println(p.age)   // -> 20

    p.age = 25       // -> age の値の変更
    println(p.age)   // -> 25
}

Kotlin ではgetterを呼び出す代わりにプロパティを直接参照します。
ミュータブルなプロパティの値の書き換えもsetter経由ではなく直接プロパティに値を渡します。


カスタムアクセサ

矩形(rectangle)を表現するクラスを定義して、その矩形が正方形かどうかを判別できるようにする場合、以下のように記述できます。

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        get() {
            return height == width
        }
}

isSquare プロパティは、値をフィールドに格納する必要はなく、この例ではカスタムのgetterの実装を持つだけとなっています。
必要な値はプロパティから値を取得するたびに毎回計算(判定)されます。

呼び出しの例は以下の通りです

fun main() {
    val r1 = Rectangle(10, 20)
    println(r1.isSuqare)    // -> 長方形なので false

    val r2 = Rectangle(100, 100)
    println(r2.isSuqare)    // -> 正方形なので true
}

このプロパティに Java からアクセスする場合は、isSquare メソッドを呼び出すことになります。


引数のない関数とカスタムgetterを持つプロパティのどちらを使うべきかについて、
どちらの選択肢も似ていて実装内容や実行速度の面では違いがありません。

違いがあるのは 可読性 だけです。
一般に、あるクラスの特徴を表現したい場合には、それをプロパティとして宣言するべきです。


まとめ

Kotlin の場合 Java に比べてかなり簡単な記述でプロパティの取り扱いができることが分かりました。



関連エントリ