Использование подстановочных знаков Java - PullRequest
4 голосов
/ 20 сентября 2011

Я хочу реализовать какую-то систему компонентов в Java.

Есть интерфейс под названием Form

interface Form<T> {
    T getObject();

    // ...
}

И я хотел бы предоставить некоторый абстрактный класс, называемый CompoundForm, дляпомогает создавать сложные формы из простых форм.

Пользователь CompoundForm должен предоставить некоторое описание каждого компонента, используя интерфейс компонента

interface Component<T, U> {
    /** Factory method to build new form for given component */
    Form<U> createForm(U u, String prefix);

    /** Extract component of type U from the compound t */
    U get(T t);

    /** Mutate t or build new compound of type T using information from u */
    T set(T t, U u);
}

С учетом этого интерфейса реализация CompoundForm выглядит примерно так:

abstract class CompoundForm<T> implements Form<T> {
    /** User should override this method and provide a collection of
      * actual components of different types, hence ? wildcard */
    protected abstract Map<String, Component<T, ?>> componentMap();

    private Map<String, Form<?>> formMap = new TreeMap<String, Form<?>>();
    private final T object;

    public CompoundForm(T object, String prefix) {
        this.object = object;
        for (Entry<String, Component<T, ?>> e: componentMap()) {
            String subPrefix = e.getKey();
            Component<T, ?> component = e.getValue();

            // !!! Compile error here: type error
            Form<?> form = component.createForm(component.get(object), prefix + subPrefix);
            formMap.put(subPrefix, form);
        }
    }

    public T getObject() {
        T result = object;
        for (Entry<String, Component<T, ?>> e: componentMap()) {
            String subPrefix = e.getKey();
            Component<T, ?> component = e.getValue();
            Form<?> form = formMap.get(subPrefix);

            // !!! Compile error here: type error
            result = component.set(result, form.getObject());
        }
        return result;
    }
}

Возможно ли реализовать что-то подобное безопасным для типов способом без непроверенных приведений?Правильно ли я использую символы подстановки?

Ответы [ 2 ]

3 голосов
/ 21 сентября 2011

Интуитивно понятен ваш код;однако ограничение в системе типов Java делает его незаконным.Давайте сначала рассмотрим более простой пример

<T> void f1(List<T> a){ ... }

<T> void f2(List<T> a1, List<T> a2){ ... }

List<?> a = ...;

f1(a); // compiles

f2(a, a); // does not compile

При компиляции f1(a) компилятор внутренне обрабатывает тип a как List<X>, где X - фиксированный, хотя и неизвестный тип.Это называется «захватом по шаблону».Передав List<X> компилятору f1, компилятор делает вывод, что T = X.

При компиляции f2(a,a) происходит нечто подобное;однако подстановочный знак применяется к двум вхождениям a по отдельности, в результате чего 1-й a имеет тип List<X1>, а 2-й a - List<X2>.Компилятор не анализирует, что a остается неизменным, поэтому X1=X2.Без этого знания передача List<X1> и List<X2> в f2() не компилируется.

Обходной путь должен заставить a появляться только один раз:

List<?> a = ...;

f2_1(a); // compiles

<T> void f2_1(List<T> a){ f2_2(a,a); } // compiles, two a's same type: List<T>

<T> void f2_2(List<T> a1, List<T> a2){ ... }

Назад к вашемуВ этом случае вам также понадобится вспомогательный метод:

<T, U> Form<U> createForm(Component<T, U> component, T object, String prefix)
{
    return component.createForm(component.get(object), prefix + subPrefix);
}

--
    Component<T, ?> component = e.getValue();

    Form<?> form = createForm(component, object, prefix + subPrefix);

Для следующей задачи вам понадобится приведение.Нет другого способа сообщить компилятору, что компонент и форма имеют одинаковые значения U.Это отношение не может быть выражено в системе типов Java, но оно гарантируется вашей логикой кода.Вы можете законно подавить предупреждение, потому что вы "проверили", чтобы убедиться, что приведение должно работать во время выполнения.

0 голосов
/ 20 сентября 2011

Посмотрите на Composite Pattern . Затем, если вы считаете, что использование дженериков полезно для вашей проблемы, прочитайте хороший учебник, такой как этот .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...