はじめに
結論から言うと、インターフェースではGetter・Setterの自動生成はでません。インターフェースの性質を理解していれば、当たり前なのですが、個人的に混乱したので、まとめます。
開発環境は以下の通りです。
- jdk 1.8.0_172
- Lombok 1.18.2
getter, setterのコピペはバグの温床
Javaのコーディングではクラスを頻繁に作ると思います。その度に毎回、以下のようなgetter, setter を定義している人は多いのではないでしょうか?(少なくとも私はそうでした)
// 普通のクラス
public class SampleClass {
// メンバ変数
private String foo;
// Getter
public String getFoo() {
return this.foo;
}
// Setter
public void setFoo(String foo) {
this.foo = foo;
}
}
// 呼び出し元
public void SampleMethod {
// 普通のクラス
SampleClass sample1 = new SampleClass();
sample1.setFoo("hoge");
System.out.println("sample1.getFoo(): " + sample1.getFoo());
}
例えば、クラスやメンバ変数(フィールド)が大量にあるとかで、こういうgetterみたいな単純なメソッドを大量にコーディングしなければならない場合、多くの人がコピペすると思います。
こういうコピペって、ミスしても気づきにくいので、バグの温床になりやすいと思います。
クラスに@Dataをつけると?
それを解決してくれるのが、Lombokです。以下のように書くだけで、getterやsetterを自動生成してくれます。(C#のプロパティみたいなもの)
import lombok.Data;
// クラスに@Dataをつける
@Data
public class SampleClassWithLombok {
// メンバ変数
private String foo;
}
// 呼び出し元
public void SampleMethod {
// @Dataのついたクラス
SampleClassWithLombok sample2 = new SampleClassWithLombok();
sample2.setFoo("hoge");
System.out.println("sample2.getFoo(): " + sample2.getFoo());
}
これで、コード量を減らせて、バグの温床もつぶすことができました。
Lombokの導入方法は、以前、こちらにまとめましたので、ご参照ください。
インターフェースに@Dataをつけると?
ここで、疑問に思ったのが、インターフェースに@Data
を付けるとどうなるのか?ということです。
インターフェースのメンバ変数にはprivate
を指定することができないので、public
なメンバ変数にgetter, setterをつけてみたいと思います。(意味はないですが)
こんな感じのインターフェースに@Dataをつけてみます。
// インターフェースに@Dataをつける
@Data
public interface SampleInterface {
// メンバ変数
public String bar = "bar";
}
つけた途端にEclipseが@Data is only supported on a class.
というエラーを吐きました。
そもそもインターフェースのメンバ変数は定数なので、setterというものを定義できないのが原因だと思います。
実装クラスに@Dataをつけると?
実装クラスではどうなのでしょうか?こんなインターフェースと実装クラスがあるとします。
// インターフェース
public interface SampleInterface {
}
import lombok.Data;
// 実装Classに@Dataをつける
@Data
public class SampleImpl implements SampleInterface {
// メンバ変数
private String foo;
}
// 呼び出し元
public void SampleMethod {
// @Dataのついた実装Class
SampleInterface sample3 = new SampleImpl();
sample3.setFoo("hoge");
System.out.println("sample3.getFoo(): " + sample3.getFoo());
}
こんな感じのコードを書いてみたところ、インターフェース・実装クラスでエラーは発生しませんでしたが、呼び出し元でメソッド setFoo(String) は型 SampleInterface で未定義です
というエラーが発生しました。
実装クラスにgetter, setterは定義されているようですが、インターフェースにその定義がないため、エラーになったようです。
ここで、インターフェースにgetter, setterを追加してみました。
// インターフェースにgetter, setterを定義する
public interface SampleInterface {
// Getter
public String getFoo();
// Setter
public void setFoo(String foo);
}
これでエラーは出なくなりました。しかし、インターフェースにgetter, setterを定義する羽目になりました。これでは、コピペミスを抑制することができませんね。
抽象クラスに@Dataをつけると?
抽象クラスではどうなのでしょうか?こんな抽象クラスと継承クラスがあるとします。
import lombok.Data;
// 抽象Classに@Dataをつける
@Data
public abstract class SampleAbstract {
// メンバ変数
private String foo;
}
//継承Class
public class SampleExtends extends SampleAbstract {
}
// 呼び出し元
public void SampleMethod {
// @Dataのついた抽象Class
SampleAbstract sample4 = new SampleExtends();
sample4.setFoo("hoge");
System.out.println("sample4.getFoo(): " + sample4.getFoo());
}
こんな感じのコードを書いてみたところ、エラーは発生しませんでした。インターフェースと違って、抽象クラスはprivate
なメンバ変数を持つことができるので、getter, setterも自動定義できるのかもしれませんね。
さいごに
通常のクラス・実装クラス・抽象クラスは、@Data
をつけることで、メンバ変数のgetter, setterを自動定義することができます。
しかし、インターフェースは、手動でgetter, setterを定義する必要があることがわかりました。
抽象クラスに切り替えれば、お手軽に回避できると思いますが、そもそもインターフェースにgetter, setterが必要になった時点で、一旦、そのインターフェースの設計を見直した方がよいのかもしれません。