覚えたら書く

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

Lombok - @Data

Lombokの@Data(lombok.Data)アノテーションの利用サンプルです。

クラスに@Dataアノテーションを付与すると、対象クラス内のインスタンス変数に対してgetter/setterでアクセスすることが可能となります。 対象のインスタンス変数をfinalで宣言している場合は、finalのメンバを引数に取るコンストラクタとgetterが使用可能となります。

@Dataアノテーションを付与すると、@Getter/@Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructorを付与したのと同じ状態となります


@Dataアノテーションを付与したクラスの動きの確認

@Dataアノテーションをパラメータの指定なしでクラスに付与します

■@Dataアノテーションを付与したクラス

import lombok.Data;

@Data
public class Person1 {

    private long id;

    private String name;

    private int age;

    private boolean isAdult;
}

■利用側のコード

サンプルコードでは以下の確認をしています

  • getterが使える
  • setterが使える
  • hashCodeがオーバーライドされている
  • toStringがオーバーライドされている
  • equalsがオーバーライドされている
public class Person1Client {

    public static void main(String[] args) {
        Person1 p1 = new Person1();

        // 対象クラスでsetterを記述してないのにsetterが使える
        p1.setId(100);
        p1.setName("Taro");
        p1.setAge(12);
        p1.setAdult(true);

        // 対象クラスでgetterを記述してないのにgetterが使える
        System.out.println("id: " + p1.getId());
        System.out.println("name: " + p1.getName());
        System.out.println("age: " + p1.getAge());
        System.out.println("isAdulet: " + p1.isAdult());

        // hashCodeも自動生成されている
        System.out.println("p1#hashCode: " + p1.hashCode());

        System.out.println("-------------");

        // メンバに同じ値を指定すればhashCodeは同じ値を返す
        Person1 p2 = new Person1();
        p2.setId(100);
        p2.setName("Taro");
        p2.setAge(12);
        p2.setAdult(true);
        System.out.println("p1#hashCode: " + p1.hashCode());
        System.out.println("p2#hashCode: " + p2.hashCode());

        Person1 p3 = new Person1();
        p3.setId(100);
        p3.setName("Taro");
        p3.setAge(12);
        p3.setAdult(true);

        System.out.println("p3#hashCode: " + p3.hashCode());

        Person1 p4 = new Person1();
        p4.setId(100);
        p4.setName("Taro");
        p4.setAge(12);
        p4.setAdult(false);

        System.out.println("p4#hashCode: " + p4.hashCode());

        System.out.println("-------------");

        // toStringの内容確認
        System.out.println("p1#toString: " + p1);
        System.out.println("p2#toString: " + p2);
        System.out.println("p3#toString: " + p3);
        System.out.println("p4#toString: " + p4);

        System.out.println("-------------");

        // equalsも自動生成されている
        System.out.println("p1 eqauls p1 -> " + p1.equals(p1));
        System.out.println("p1 eqauls p2 -> " + p1.equals(p2));
        System.out.println("p1 eqauls p3 -> " + p1.equals(p3));
        System.out.println("p1 eqauls p3 -> " + p1.equals(p4));
        System.out.println("p2 eqauls p3 -> " + p2.equals(p3));
        System.out.println("p2 eqauls p4 -> " + p2.equals(p4));
        System.out.println("p3 eqauls p4 -> " + p3.equals(p4));
    }
}

■実行結果

id: 100
name: Taro
age: 12
isAdulet: true
p1#hashCode: 490905642
-------------
p1#hashCode: 490905642
p2#hashCode: 490905642
p3#hashCode: 490905642
p4#hashCode: 490905660
-------------
p1#toString: Person1(id=100, name=Taro, age=12, isAdult=true)
p2#toString: Person1(id=100, name=Taro, age=12, isAdult=true)
p3#toString: Person1(id=100, name=Taro, age=12, isAdult=true)
p4#toString: Person1(id=100, name=Taro, age=12, isAdult=false)
-------------
p1 eqauls p1 -> true
p1 eqauls p2 -> true
p1 eqauls p3 -> true
p1 eqauls p3 -> false
p2 eqauls p3 -> true
p2 eqauls p4 -> false
p3 eqauls p4 -> false

■実際生成されているソースコード

実際に生成されているソースコードをdelombokを使って確認してみました

public class Person1 {
    private long id;
    private String name;
    private int age;
    private boolean isAdult;

    public Person1() {
    }

    public long getId() {
        return this.id;
    }

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

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

    public boolean isAdult() {
        return this.isAdult;
    }

    public void setId(final long id) {
        this.id = id;
    }

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

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

    public void setAdult(final boolean isAdult) {
        this.isAdult = isAdult;
    }

    @Override
    public boolean equals(final Object o) {
        if (o == this) return true;
        if (!(o instanceof Person1)) return false;
        final Person1 other = (Person1) o;
        if (!other.canEqual((Object) this)) return false;
        if (this.getId() != other.getId()) return false;
        final Object this$name = this.getName();
        final Object other$name = other.getName();
        if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
        if (this.getAge() != other.getAge()) return false;
        if (this.isAdult() != other.isAdult()) return false;
        return true;
    }

    protected boolean canEqual(final Object other) {
        return other instanceof Person1;
    }

    @Override
    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final long $id = this.getId();
        result = result * PRIME + (int) ($id >>> 32 ^ $id);
        final Object $name = this.getName();
        result = result * PRIME + ($name == null ? 43 : $name.hashCode());
        result = result * PRIME + this.getAge();
        result = result * PRIME + (this.isAdult() ? 79 : 97);
        return result;
    }

    @Override
    public String toString() {
        return "Person1(id=" + this.getId() + ", name=" + this.getName() + ", age=" + this.getAge() + ", isAdult=" + this.isAdult() + ")";
    }
}


@Dataを付与したクラスにfinalフィールドがある場合

対象クラスに@Dataアノテーションを付与していて、そのクラスにfinalフィールドが存在していると、
自動的にfinalフィールドをセットするための引数付きコンストラクタが定義されます

■@Dataアノテーションを付与したクラス(一部のフィールドがfinal)

import lombok.Data;

@Data
// id は必須
public class Person4 {

    private final long id;

    private String name;

    private int age;
}

■IDEでの表示状態

f:id:nini_y:20160926222948p:plain

■利用側のコード

public class Person4Client {

    public static void main(String[] args) {
        long id = 5;

        // finalフィールドへの値を設定するための引数付きコンストラクタが定義される
        Person4 p = new Person4(id);
        p.setName("Sample Hanako");
        p.setAge(24);
        // p.setId(100L); <-- finalフィールドへのsetterは定義されない

        System.out.println(p);

        // Person4 p2 = new Person4(); <-- デフォルトコンストラクタは定義されない
    }
}

■実行結果

Person4(id=5, name=Sample Hanako, age=24)


staticファクトリメソッドを定義する

@Dataアノテーションに、staticConstructorパタメータを指定することで、対象クラスのインスタンスを生成するためのファクトリメソッドを定義することができます

■@Dataアノテーションを付与したクラス

import lombok.Data;

// ファクトリメソッドの名前を of とする
@Data(staticConstructor="of")
public class Person3 {

    private final long id;

    private String name;

    private int age;
}

■利用側のコード

public class Person3Client {

    public static void main(String[] args) {
        long id = 101;
        Person3 p = Person3.of(id); // staticファクトリメソッドが定義されている

        p.setName("Taro");
        p.setAge(12);

        // 対象クラスでgetterを記述してないのにgetterが使える
        System.out.println("Person3#toString: " + p);

        // Person3 p1 = new Person3(200);  <-- コンストラクタの呼び出しはできない
    }
}

■実行結果

Person3#toString: Person3(id=101, name=Taro, age=12)



関連エントリ