[毎日Kotlin] Day7. Data classes(データクラス)

2018.01.22

はじめに

毎日Kotlinシリーズです。

このシリーズを初めての方はこちらです。「毎日Kotlin」はじめました | Developers.IO

問題

Data classes | Try Kotlin

Data Classを使ってみよう。

Rewrite the following Java code to Kotlin:

public 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;
    }
}

Then add a modifier data to the resulting class. This annotation means the compiler will generate a bunch of useful methods in this class: equals/hashCode, toString and some others. The getPeople function should start to compile.

Read about classes, properties and data classes.

class Person

fun getPeople(): List<Person> {
    return listOf(Person("Alice", 29), Person("Bob", 31))
}

狙い

ここで考えて欲しい問題の意図はなんだろうか?

Data classはValueオブジェクトとして使うと便利です。Javaで書くと面倒だった部分をよしなにしてくれます。Data Classで何を自動でしてくれるかイメージをつかもう。

かなり便利で使い所がたくさんあるので、覚えよう!

解答例

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

通常のclassにdataをつけるだけです。

補足

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

  val person = Person("kamedon", 30)
  println(Person("kamedon", 30) == Person("kamedon", 30))
  println(Person("kamedon", 30) == person)
  println(Person("kamedon", 30) === Person("kamedon", 30))
  println(Person("kamedon", 30) === person)

true or false どちらが表示されるでしょうか?

ヒント:== は、インスタンスのメンバーの値が同じか、=== は、同じ参照かです

上から、true,true,false,falseです。

これが、ただのclassの時はどうでしょうか?

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

  val person = Person("kamedon", 30)
  println(Person("kamedon", 30) == Person("kamedon", 30))
  println(Person("kamedon", 30) == person)
  println(Person("kamedon", 30) === Person("kamedon", 30))
  println(Person("kamedon", 30) === person)

すべてfalseになります。

つまり、data classをJavaでいうところの、equals、hashCode、toStringをOverrideしインスタンスのメンバー変数の値が同じ時にtrueを返すように自動的に実装してくれます。

Person.ktをJavaに戻すと以下の通り。

public final class Person {
   @NotNull
   private final String name;
   private final int age;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final int getAge() {
      return this.age;
   }

   public Person(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
      this.age = age;
   }

   @NotNull
   public final String component1() {
      return this.name;
   }

   public final int component2() {
      return this.age;
   }

   @NotNull
   public final Person copy(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      return new Person(name, age);
   }

   // $FF: synthetic method
   // $FF: bridge method
   @NotNull
   public static Person copy$default(Person var0, String var1, int var2, int var3, Object var4) {
      if((var3 & 1) != 0) {
         var1 = var0.name;
      }

      if((var3 & 2) != 0) {
         var2 = var0.age;
      }

      return var0.copy(var1, var2);
   }

   public String toString() {
      return "Person(name=" + this.name + ", age=" + this.age + ")";
   }

   public int hashCode() {
      return (this.name != null?this.name.hashCode():0) * 31 + this.age;
   }

   public boolean equals(Object var1) {
      if(this != var1) {
         if(var1 instanceof Person) {
            Person var2 = (Person)var1;
            if(Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}

インスタンスのメンバー変数の一部だけ変更して新しいインスタンスを作るのときに便利なcopyメソッド。

val person = Person("kamedon", 30)
   val person2 = person.copy(age = 40) // name: Kamedon , age: 40

componentNも実装されます。これはメンバー変数の1つ目を返すcomponent1。(personの場合はname)メンバー変数の2つ目を返すcomponent2。(personの場合はage)それぞれの値を取り出したいとき便利です。

val person = Person("kamedon", 30)
   val (name, age) = person
   println(name)
   println(age)

あとがき

とっても便利なので、どんどん使っていこう!

Day8.でまたお会いしましょう。