#java #immutability
#java #неизменяемость
Вопрос:
Я экспериментирую со способами создания неизменяемых объектов. Следующие объекты builder довольно привлекательны, потому что они сохраняют роль аргументов ясной. Однако я хотел бы использовать компилятор для проверки того, что определенные поля установлены, например, при Immutable()
вызове конструктора. StrictImmutableBuilder
обеспечивает эти проверки, но является довольно шумным. Есть ли какой-нибудь способ получить те же проверки, но в форме LaxImmutableBuilder
? Возможно, с помощью аннотаций?
public class Immutable {
public static void main(String[] args) {
new Immutable("13272873C", 23, false);
// nice but what where those arguments?
new LaxImmutableBuilder() {{
refCode("13272873C");
age(23);
subscribed(false);
}}.build();
// now I know what each value represents
// but what if I forgot to set one?
new StrictImmutableBuilder() {
public String refCode() { return "13272873C"; }
public int age() { return 23; }
public boolean subscribed() { return false; }
}.build();
// now I'm forced to set each field, but now
// we have the extra noise of "return"
// and also "public" if we want to use
// this outside the current package
// is there another way? maybe using annotations?
}
private final String refCode;
private final int age;
private final boolean subscribed;
public String getRefCode() {
return refCode;
}
public int getAge() {
return age;
}
public boolean isSubscribed() {
return subscribed;
}
public Immutable(String a, int b, boolean c) {
this.refCode = a;
this.age = b;
this.subscribed = c;
}
}
abstract class StrictImmutableBuilder {
public abstract String refCode();
public abstract int age();
public abstract boolean subscribed();
public Immutable build() {
return new Immutable(refCode(), age(), subscribed());
}
}
abstract class LaxImmutableBuilder {
private String refCode;
private int age;
private boolean subscribed;
protected void refCode(String refCode) {
this.refCode = refCode;
}
protected void age(int age) {
this.age = age;
}
protected void subscribed(boolean subscribed) {
this.subscribed = subscribed;
}
public Immutable build() {
return new Immutable(refCode, age, subscribed);
}
}
Ответ №1:
Вот шаблон, который я использую:
class YourClass {
// these are final
private final int x;
private final int y;
private int a;
private int b;
// finals are passed into the constructor
private YourClass(int x, int y) {
this.x = x;
this.y = y;
}
public static class Builder {
// int x, int y, int a, int b
// whatever's final is passed into constructor
public Builder(int x, int y) {
this.x = x;
this.y = y;
}
// a and b are optional, so have with() methods for these
public Builder withA(int a) {
this.a = a;
return this;
}
public Builder withB(int b) {
this.b = b;
return this;
}
public YourClass build() {
YourClass c = new YourClass (x, y);
c.a = a;
c.b = b;
return c;
}
}
}
Комментарии:
1. 1: Я поступаю аналогично, но вместо того, чтобы писать конструктор, я генерирую его из базового неизменяемого класса. Это помогает поддерживать согласованность классов.
2. @PeterLawrey Я предполагаю, что вы используете инструмент для выполнения этой генерации, если да, то какой?
3. Спасибо! Это хороший шаблон, если ваши объекты имеют необязательные свойства. Я полагаю, внутренний класс также помогает людям найти конструктор для данного типа.
4. Я настроил живой шаблон IntelliJ, поэтому, когда я нажимаю «b», «tab», он автоматически генерирует для меня метод with (), что упрощает написание этих вещей
Ответ №2:
есть такой трюк: шаблон построения, безопасный для типов
http://michid.wordpress.com/2008/08/13/type-safe-builder-pattern-in-java/
но это просто слишком безумно.
Комментарии:
1. Спасибо. Хорошая находка. Я почти достаточно сумасшедший, чтобы использовать это! 🙂 Но на самом деле это не масштабируется до большого количества аргументов.