Универсальная фабрика универсальных контейнеров - PullRequest
4 голосов
/ 24 ноября 2011

У меня есть общий абстрактный класс Factory<T> с методом createBoxedInstance(), который возвращает экземпляры T, созданные реализациями createInstance(), обернутыми в общий контейнер Box<T>.

abstract class Factory<T> {
    abstract T createInstance();

    public final Box<T> createBoxedInstance() {
        return new Box<T>(createInstance());
    }

    public final class Box<T> {
        public final T content;

        public Box(T content) {
            this.content = content;
        }
    }
}

В некоторых моментах мне нужен контейнер типа Box<S>, где S является предком T.Можно ли сделать createBoxedInstance() сам по себе универсальным, чтобы он возвращал экземпляры Box<S>, где вызывающий выбрал S?К сожалению, определение функции следующим образом не работает, поскольку параметр типа не может быть объявлен с использованием ключевого слова super , только используется .

public final <S super T> Box<S> createBoxedInstance() {
    return new Box<S>(createInstance());
}

Единственная альтернатива, которую я вижу, - это сделать все места, где требуется экземпляр Box<S> accept Box<? extends S>, что делает элемент содержимого контейнера назначаемым S.

Есть ли способ обойти это безпереназначить экземпляры T в контейнеры типа Box<S>?(Я знаю, что мог бы просто бросить Box<T> на Box<S>, но я чувствовал бы себя очень, очень виновным.)

Ответы [ 3 ]

2 голосов
/ 25 ноября 2011

Попробуйте переписать ваш другой код, чтобы не использовать Box<S>, а вместо этого использовать Box<? extends S>, поэтому он также примет Box<T>.Таким образом, вы делаете явным, что Box может также содержать подклассы S.

. Следующее также должно работать, если вы делаете Box static:

public static <S, T extends S> Box<S> createBoxedInstance(Factory<T> factory) {
  return new Box<S>(factory.createInstance());
}

Однако, он может быть не в состоянии сделать вывод типа для S, и в этот момент вам нужно:

public static <S, T extends S> Box<S> createBoxedInstance(Factory<T> factory, S dummy) {
  return new Box<S>(factory.createInstance());
}

Еще один вариант, который очень явный:

public static <S, T extends S> Box<? extends S> createBoxedInstance(Test<T> factory, S dummy) {
  return factory.createBoxedInstance(); // Actually a Box<T>
}
0 голосов
/ 28 ноября 2011

Причина, по которой вы не можете этого сделать (или вам приходится использовать странные преобразования), заключается в следующем. Представьте, что у содержимого Box вместо финального также есть сеттер.Кроме того, давайте предположим, что тип S является супертипом как R, так и T. Следующий код будет действителен без приведения.

Box<T> boxT = new Box<T>(objectT);
Box<S> boxS = boxT;
S objectR = new R();
boxS.set(objectR);

Этот код будет действителен без предупреждений, за исключением того, что установщик потерпит неудачуво время выполнения с неожиданным исключением (неожиданным в том смысле, что не очевидно, что исключение приведения может быть выдано), потому что вы не можете назначить что-либо с типом R чему-то типа T.

Вы можете прочитать большена http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#Topic3 (но если рядом есть аспирин, у вас начнется головная боль)

Как многие говорили, вероятно, лучше использовать box в другом коде.

0 голосов
/ 26 ноября 2011

Вы можете достичь желаемого в некоторой степени:

public final <S extends Box<? super T>> S createBoxedInstance() {
    // sadly we can't capture the wildcard in the bound of S
    // this means we have to cast, but at least it's local..
    // *should* this cast ever break due to reification, we
    // can fix it here and be good to go
    @SuppressWarnings("unchecked")
    S box = (S) new Box<T>(createInstance());
    return box;
}

Factory<Integer> factory = getFactory();
Box<Number> box1 = factory.createBoxedInstance();
Box<Object> box2 = factory.createBoxedInstance();

Я думаю, что ваш собственный Box<? extends S> - Альтернативный подход. Однако, если это заставит код клиента заполняться символами подстановки, вышеприведенный может быть предпочтительным.

...