最近、Joshua BlochのEffective Javaを読み始めました。 Builderパターン[この本の項目2]のアイデアは非常に興味深いことがわかりました。プロジェクトに実装しようとしましたが、コンパイルエラーがありました。以下は、私がやろうとしていたことです。
複数の属性を持つクラスとそのビルダークラス:
public class NutritionalFacts {
private int sodium;
private int fat;
private int carbo;
public class Builder {
private int sodium;
private int fat;
private int carbo;
public Builder(int s) {
this.sodium = s;
}
public Builder fat(int f) {
this.fat = f;
return this;
}
public Builder carbo(int c) {
this.carbo = c;
return this;
}
public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}
private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}
上記のクラスを使用しようとするクラス:
public class Main {
public static void main(String args[]) {
NutritionalFacts n =
new NutritionalFacts.Builder(10).carbo(23).fat(1).build();
}
}
次のコンパイラエラーが発生します。
effectivejava.BuilderPattern.NutritionalFacts.Builderを含むインスタンスが必要ですNutritionalFacts n = new NutritionalFacts.Builder(10).carbo(23).fat(1).build();
メッセージの意味がわかりません。説明してください。上記のコードは、Blochの本で提案された例に似ています。
ビルダーをstatic
クラスにします。その後、動作します。静的でない場合、その所有クラスのインスタンスが必要になります-そして、ポイントはそのインスタンスを持たないことであり、ビルダーなしでインスタンスを作成することさえ禁止することです。
public class NutritionFacts {
public static class Builder {
}
}
参照: ネストされたクラス
Builderクラスを静的として作成する必要があります。また、フィールドをfinalにして、これらの値を取得するゲッターを用意する必要があります。これらの値にセッターを提供しないでください。このようにして、クラスは完全に不変になります。
public class NutritionalFacts {
private final int sodium;
private final int fat;
private final int carbo;
public int getSodium(){
return sodium;
}
public int getFat(){
return fat;
}
public int getCarbo(){
return carbo;
}
public static class Builder {
private int sodium;
private int fat;
private int carbo;
public Builder sodium(int s) {
this.sodium = s;
return this;
}
public Builder fat(int f) {
this.fat = f;
return this;
}
public Builder carbo(int c) {
this.carbo = c;
return this;
}
public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}
private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}
そして、次のようにプロパティを設定できます。
NutritionalFacts n = new NutritionalFacts.Builder().sodium(10).carbo(15).
fat(5).build();
Intellij IDEAでインナービルダーを生成するには、次のプラグインを確認してください。 https://github.com/analytically/innerbuilder
静的な方法で非静的クラスにアクセスしようとしています。 Builder
をstatic class Builder
に変更すると、動作するはずです。
Builder
のインスタンスが存在しないため、指定した使用例は失敗します。すべての実用的な目的のための静的クラスは常にインスタンス化されます。静的にしない場合は、次のように言う必要があります。
Widget = new Widget.Builder(10).setparm1(1).setparm2(3).build();
毎回新しいBuilder
を作成する必要があるためです。
アイデアが得られたら、実際には、ロンボクの@Builder
の方がはるかに便利であることがわかります。
@Builder
を使用すると、次のようなコードでクラスをインスタンス化するために必要なコードを自動的に生成できます。
Person.builder()
.name("Adam Savage")
.city("San Francisco")
.job("Mythbusters")
.job("Unchained Reaction")
.build();
これは、囲むタイプを作成できないことを意味します。つまり、最初に「親」クラスのインスタンスを作成する必要があり、このインスタンスからネストされたクラスインスタンスを作成できます。
NutritionalFacts n = new NutritionalFacts()
Builder b = new n.Builder(10).carbo(23).fat(1).build();
Builderクラスは静的である必要があります。現在、それ以上のコードを実際にテストする時間はありませんが、うまくいかない場合はお知らせください。もう一度見てみましょう。
個人的に、2つの異なるクラスがある場合は、他のアプローチを使用することを好みます。したがって、静的クラスは必要ありません。これは基本的に、新しいインスタンスを作成する必要がある場合にClass.Builder
の書き込みを避けるためです。
public class Person {
private String attr1;
private String attr2;
private String attr3;
// package access
Person(PersonBuilder builder) {
this.attr1 = builder.getAttr1();
// ...
}
// ...
// getters and setters
}
public class PersonBuilder (
private String attr1;
private String attr2;
private String attr3;
// constructor with required attribute
public PersonBuilder(String attr1) {
this.attr1 = attr1;
}
public PersonBuilder setAttr2(String attr2) {
this.attr2 = attr2;
return this;
}
public PersonBuilder setAttr3(String attr3) {
this.attr3 = attr3;
return this;
}
public Person build() {
return new Person(this);
}
// ....
}
したがって、次のようにビルダーを使用できます。
Person person = new PersonBuilder("attr1")
.setAttr2("attr2")
.build();
Builder classをstatic class Builderに変更する必要があります。その後、正常に動作します。
ここですでに述べたように、クラスをstatic
にする必要があります。ちょっとした追加-必要に応じて、静的な方法を使用せずに少し異なる方法があります。
このことを考慮。クラス内でwithProperty(value)
型セッターのようなものを宣言してビルダーを実装し、それらへの参照を返すようにします。このアプローチでは、スレッドセーフで簡潔な単一のエレガントなクラスがあります。
このことを考慮:
public class DataObject {
private String first;
private String second;
private String third;
public String getFirst(){
return first;
}
public void setFirst(String first){
this.first = first;
}
...
public DataObject withFirst(String first){
this.first = first;
return this;
}
public DataObject withSecond(String second){
this.second = second;
return this;
}
public DataObject withThird(String third){
this.third = third;
return this;
}
}
DataObject dataObject = new DataObject()
.withFirst("first data")
.withSecond("second data")
.withThird("third data");
Java Builder の例についてもっと調べてください。