2014-08-10 1 views
0

У меня есть следующие настройки определяются следующим образом:Как сделать шаблон посетителя более расширяемым, создав элементы подтипа, требующим определенных посетителей подтипа через дженерики?

public interface Element { 
    public <R> R accept(Visitor<R> visitor); 
} 

public interface Visitor<R> { 

} 

class SpecificElement implements Element { 
    @Override         // ERROR: didn't override accept in parent 
    public <R> R accept(SpecificVisitor visitor) { 
     return visitor.visitSpecificElement(this); 
    } 
} 

class SpecificVisitor implements Visitor<Boolean> { 
    public Boolean visitSpecificElement(SpecificElement element) { 
     return true; 
    } 
} 

Как я могу изменить настройки выше, так что я могу требовать подтипа SpecificElement только принимать SpecificVisitor и возможности переопределить родительский класс accept метода.

То, что я хочу, чтобы это произошло, чтобы санкционировать, что класс реализации SpecificElement будет принимать только гостей типа SpecificVisitor и ничего, но по-прежнему держать подпись интерфейса типа Element «s на Visitor.

UPDATE 1: я добавил параметр типа V extends Visitor<?> в Element, а затем расширить его SpecificElement extends Element<SpecificVisitor<?> и был в состоянии получить то, что я хочу (более строгие типы параметров в методе принимают): <R> R accept(SpecificVisitor<?> visitor). Однако теперь моя проблема заключается в том, что я хочу определить возвращаемый тип метода accept на основе параметра типа посетителя аргумента (в данном случае ?). Изменение ? на R вызывает ошибку компиляции.

public interface Element<V extends Visitor<?> { 
    public <R> R accept(Visitor<R> visitor); 
} 

public interface Visitor<R> { 

} 

class SpecificElement implements Element<SpecificVisitor<?>> { 
    @Override          
    public <R> R accept(SpecificVisitor<R> visitor) { // error in type parameter R in SpecificVisitor 
     return visitor.visitSpecificElement(this); 
    } 
} 

class SpecificVisitor implements Visitor<Boolean> { 
    public Boolean visitSpecificElement(SpecificElement element) { 
     return true; 
    } 
} 

UPDATE 2: Я хотел бы написать семейство посетителей для семьи элементов, не отказываясь от типобезопасности. Обратите внимание на использование одного и того же подтипа Visitor для поддерева Element. Пример:

--- Element -> Visitor<R> 
     | 
     |---- SpecificElement -> SpecificVisitor<R> 
     |   | 
     |   |---------SpecificElementA -> SpecificVisitor<R> 
     |   | 
     |   |---------SpecificElementB -> SpecificVisitor<R> 
     | 
     |---- FancyElement -> FancyVisitor<R> 
        | 
        |---------FancyElementA -> FancyVisitor<R> 
        | 
        |---------FancyElementB -> FancyVisitor<R> 
+1

Я не уверен, есть ли хорошее решение для этого без множественных ограничений типа, которые включают переменные типа или более высокие типы, ни одна из которых не поддерживается Java. –

+0

Возможно, это поможет людям найти подходящие обходы, если вы включили информацию или примеры того, как это будет полезно? (По сравнению с простым определением 'SpecificElement' и' SpecificVisitor' без суперклассов.) –

+0

@DanGetz В настоящее время я склонен думать, что это не поддерживается в Java. Но я добавил больше объяснений относительно того, что я хочу, чтобы произойти выше. – Chad

ответ

1

Это может быть выполнено, если вы знаете свои подтипы, которые хотите использовать заранее. (. Вы используете шаблон посетителя, так что вы должны знать все типы элементов заранее, во всяком случае) Важно, чтобы построить свой интерфейс Visitor снизу вверх, а не сверху вниз:

public interface ElementVisitor<R> extends SpecificVisitor<R>, FancyVisitor<R> { 
} 

public interface Element { 
    public <R> R accept(ElementVisitor<R> visitor); 
} 

Теперь Element подтипов потребуются немного шаблонные, так что они могут реализовать интерфейс Element:

public abstract class FancyElement implements Element { 
    public abstract <R> R accept(FancyVisitor<R> visitor); 

    public <R> R accept(ElementVisitor<R> visitor) { 
     return accept((FancyVisitor<R>) visitor); 
    } 
} 

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

public class FancyElement1 extends FancyElement { 
    public <R> R accept(FancyVisitor<R> visitor) { 
     return visitor.visit(this); 
    } 
} 

public interface FancyVisitor<R> { 
    public R visit(FancyElement1 e); 
    // ... 
} 

... и повторить для SpecificVisitor.

Теперь вы можете определить либо FancyVisitor, который может посетить только FancyElement S, A SpecificVisitor, который может посетить только SpecificElement с, или ElementVisitor, которые могут посетить все Element с.

0

Возможно, эта версия будет полезна.

сделать интерфейс Element родового

interface Element<V> { 
    <R> R accept(V visitor); 
} 

и SpecificElement должно быть так:

public class SpecificElement implements Element<SpecificVisitor> { 
    public <R> R accept(SpecificVisitor visitor) { 
     return visitor.visit(); 
    } 
} 

отмечает, что Element_s общего типа V не является строгим типом, может быть что угодно. разработчик интерфейса Element, может писать любой класс в нем, даже если он не реализует интерфейс Visitor. это недостаток этого кода.

+0

Могу ли я ограничить V таким, чтобы он был типом, расширяющим интерфейс '' Visitor''? – Chad

+1

Теперь моя проблема заключается в том, что мой посетитель использовал параметр типа '' R'', который предоставляется на уровне метода. '' R'' на уровне метода сталкивается с '' '' 'в' 'SpecificVisitor ' 'на уровне класса. Есть ли какое-то обходное решение? – Chad

+1

Обратите внимание, что тип возврата accept зависит от типа SpecificVisitor – Chad