Как избежать избыточности кода конструктора в Java?
У меня есть следующий класс:
class Pair
{
String car;
Integer cdr;
public Pair () {}
public Pair (String car) { this.car = car; }
public Pair (Integer cdr) { this.cdr = cdr; }
public Pair (String car, Integer cdr)
{
this(car);
this(cdr);
}
}
Класс содержит два необязательных значения, и я хотел бы предоставить все возможные перестановки конструктора. Первая версия ничего не инициализирует, вторая инициализирует только первое значение, а третье инициализирует только второе значение.
Последний конструктор представляет собой комбинацию второго и третьего. Но записать это невозможно, потому что сбой кода с.
constructor.java:13: call to this must be first statement in constructor
this(cdr);
^
1 error
Можно ли написать последний конструктор без избыточности кода (также без вызова тех же методов setter)?
Ответы
Ответ 1
Как правило, конструкторы с меньшим количеством аргументов должны вызывать те, у которых больше.
public Pair() {}
public Pair(String car) { this(car, null); }
public Pair(Integer cdr) { this(null, cdr); }
public Pair(String car, Integer cdr) { this.car = car; this.cdr = cdr; }
Ответ 2
Цепь ваших конструкторов в противоположном направлении, причем наиболее конкретным является тот, который задает все поля:
public Pair() {
this(null, null); // For consistency
}
public Pair(String car) {
this(car, null);
}
public Pair(Integer cdr) {
this(null, cdr);
}
public Pair (String car, Integer cdr) {
this.car = car;
this.cdr = cdr;
}
Таким образом:
- Только одно место устанавливает поля и устанавливает все поля
- Из любого другого конструктора вы можете указать (и указать, когда вы читаете код) значения "по умолчанию" для других полей.
В стороне, я настоятельно рекомендую вам сделать поля частными (и, возможно, окончательными), и дать им более значимые имена.
Обратите внимание, что таким образом, если у вас есть (скажем) 5 параметров и один конструктор с 3, один с 4 и один с 5, вы можете выбрать цепочку из 3 → 4 → 5, или вы можете перейти прямо из 3 → 5.
Кроме того, вам может потребоваться полностью удалить однопараметрические конструкторы - было бы более читаемым вместо этого использовать статические методы, где вы можете указать значение в имени:
public static Pair fromCar(String car) {
return new Pair(car, null);
}
public static Pair fromCdr(Integer cdr) {
return new Pair(null, cdr);
}
Или в моем предпочтительном наименовании значений:
public static Pair fromFirst(String first) {
return new Pair(first, null);
}
public static Pair fromSecond(Integer second) {
return new Pair(null, second);
}
В этот момент вы можете сделать класс Pair
общим, не беспокоясь о том, какой конструктор будет вызываться, если два аргумента типа одинаковы. Кроме того, любой, кто читает код, может понять, что будет создано, не проверяя тип аргумента.
Ответ 3
Вы, вероятно, ищете шаблон конструктора здесь.
Кроме всего прочего, этот шаблон позволяет вам не иметь конструкторов bazillion, охватывающих все случаи. Для вашей ситуации это может быть:
@Immutable // see JSR 305
public final class Pair
{
private final String car;
private final integer cdr;
private Pair(final Builder builder)
{
car = builder.car;
cdr = builder.cdr;
}
public static Builder newBuilder()
{
return new Builder();
}
// whatever other methods in Pair, including accessors for car and cdr, then:
@NotThreadSafe // see JSR 305
public final class Builder
{
private String car;
private int cdr;
private Builder()
{
}
public Builder withCar(final String car)
{
this.car = car;
return this;
}
public Builder withCdr(final int cdr)
{
this.cdr = cdr;
return this;
}
public Pair build()
{
return new Pair(this);
}
}
}
Использование образца:
final Pair newPair = Pair.newBuilder.withCar("foo").withCdr(1).build();
Преимущество: Pair
теперь неизменно!
Ответ 4
class Pair
{
String car;
Integer cdr;
public Pair () {}
public Pair (String car) {
this(car, null)
}
public Pair (Integer cdr) {
this(null, cdr);
}
public Pair (String car, Integer cdr) {
this.car = car;
this.cdr = cdr;
}
}