Шаблон на 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), където методът 'cheese' връща Builder, а не Builder‹Q›. - person Cowan; 18.05.2010