Шаблон Java Builder с границами универсального типа

Я пытаюсь создать класс со многими параметрами, используя шаблон Builder, а не телескопические конструкторы. Я делаю это способом, описанным в книге Джошуа Блоха «Эффективная Java», имея частный конструктор для включающего класса и общедоступный статический класс Builder. Класс Builder гарантирует, что объект находится в согласованном состоянии перед вызовом build (), после чего делегирует создание закрывающего объекта частному конструктору. Таким образом

public class Foo {

    // Many variables

    private Foo(Builder b) {
        // Use all of b's variables to initialize self
    }

    public static final class Builder {

        public Builder(/* required variables */) {

        }

        public Builder var1(Var var) {
            // set it
            return this;
        }

        public Foo build() {
            return new Foo(this);
        }

    }

}

Затем я хочу добавить границы типов к некоторым переменным и, следовательно, мне нужно параметризовать определение класса. Я хочу, чтобы границы класса Foo были такими же, как у класса Builder.

public class Foo<Q extends Quantity> {

    private final Unit<Q> units;
    // Many variables

    private Foo(Builder<Q> b) {
        // Use all of b's variables to initialize self
    }

    public static final class Builder<Q extends Quantity> {
        private Unit<Q> units;

        public Builder(/* required variables */) {

        }

        public Builder units(Unit<Q> units) {
            this.units = units;
            return this;
        }

        public Foo build() {
            return new Foo<Q>(this);
        }

    }

}

Это нормально компилируется, но компилятор позволяет мне делать то, что, по моему мнению, должно быть ошибкой компилятора. Например.

public static final Foo.Builder<Acceleration> x_Body_AccelField =
        new Foo.Builder<Acceleration>()
        .units(SI.METER)
        .build();

Здесь аргумент единиц измерения не Unit<Acceleration>, а Unit<Length>, но он все равно принимается компилятором.

Что я здесь делаю не так? Я хочу убедиться во время компиляции, что типы модулей правильно совпадают.


person I82Much    schedule 17.05.2010    source источник


Ответы (2)


units должен возвращать Builder<Q>, а не неподтвержденный Builder.

person Daniel Martin    schedule 17.05.2010

Хотя точка @Daniel действительна, по крайней мере, Eclipse обнаруживает ошибку в вашем коде. Конечно, ваше определение Quantity, Unit и METER, вероятно, отличается от упрощенного взлома, который я собрал:

interface Quantity {
}
class Acceleration implements Quantity {
}
class Length implements Quantity {
}
public class Unit<Q extends Quantity> {
    public static final Unit<Length> METER = new Unit<Length>();
}

public static final Foo.Builder<Acceleration> x_Body_AccelField =
    new Foo.Builder<Acceleration>()
    .units(Unit.METER) // here the compiler complains
    .build();

Сообщение об ошибке:

The method units(Unit<Acceleration>) in the type Foo.Builder<Acceleration> is
not applicable for the arguments (Unit<Length>)
person Péter Török    schedule 17.05.2010
comment
Интересный. Я использовал NetBeans, который не жаловался. Иерархия модулей и т. Д., Которую я использую, взята из JSR 275 (download.java.net/maven/2/net/java/dev/jsr-275/jsr-275/) и JScience (jscience.org) - person I82Much; 18.05.2010
comment
Вы должны получать эту ошибку даже в NetBeans ... если нет, это очень серьезная ошибка. Разница в исходном коде заключается в том, что у вас есть промежуточное свойство: new Foo.Builder ‹Acceleration› () .cheese (GOUDA) .units (Unit.METER), где метод «сыра» возвращает Builder, а не Builder ‹Q›. - person Cowan; 18.05.2010