membersound
membersound

Reputation: 86845

Typesafe of generics in Visitor and Strategy Pattern

I'm trying to create a Visitor<T> that returns a certain strategy for an instanceof object. It already works, but I cannot get the Generics typesafe. What am I missing i the following code:

class Base;
class Foo extends Base;
class Bar extends Base;

interface Visitor<T> {
    T visit (Foo foo);
    T visit (Bar bar);
}

interface Strategy<T> {
    void action(T obj);
}

class FooStrategy implements Strategy<Foo> {
    @Override
    void action(Foo foo) {
        //perform strategy action on foo
    }
}

//References to generic type Strategy<T> should be parameterized
class MyVisitor implements Visitor<Strategy> {

    //References to generic type Strategy<T> should be parameterized
    @Override
    Strategy visit(Foo foo) {
        return new FooStrategy();
    }

    //References to generic type MyStrategy<T> should be parameterized
    @Override
    Strategy visit(Bar bar) {
        return new BarStrategy();
    }
}

In the class MyVisitor eclipse keeps complaining that Strategy<T> should be parameterized. What am I missing to make this generic construct typesafe?

Update:

I want to use it like this:

        Visitor<Strategy> visitor = new MyVisitor();
        Strategy strat = drawable.accept(visitor);

        //use the strategy
        Base object; //may be a Foo or Bar or whatever
        strat.action(object);

Upvotes: 1

Views: 693

Answers (2)

Rob
Rob

Reputation: 11733

This isn't a Visitor. And even if it were, this is not a correct use for one. Visitor uses double dispatch; this does not have that.

Upvotes: 0

kennytm
kennytm

Reputation: 523544

You could put a ? extends Base to the type parameter to indicate that it's a strategy of whatever type you don't care, but it must be a subclass of Base.

class MyVisitor implements Visitor<Strategy<? extends Base>> {
  @Override
  Strategy<? extends Base> visit(Foo foo) {
    return new FooStrategy();
  }
  @Override
  Strategy<? extends Base> visit(Bar bar) {
    return new BarStrategy();
  }
}

    Visitor<Strategy<? extends Base>> visitor = new MyVisitor();
    Strategy<? extends Base> strat = drawable.accept(visitor);

However, you are not allowed to call a strategy like that:

    //use the strategy
    Base object; //may be a Foo or Bar or whatever
    strat.action(object);  // <--- error.

because the action method effectively has a signature of

<U extends Base> void action(U object);

i.e. the type of the argument may require an arbitrary subclass of Base, and the only type which can be passed to any arbitrary subclass of Base is null.

To solve this, simply make FooStrategy and BarStrategy implement Strategy<Base>:

class FooStrategy implements Strategy<Base> { void action(Base obj) { ... } }
class BarStrategy implements Strategy<Base> { void action(Base obj) { ... } }

class MyVisitor implements Visitor<Strategy<Base>> {
  @Override
  Strategy<Base> visit(Foo foo) {
    return new FooStrategy();
  }
  @Override
  Strategy<Base> visit(Bar bar) {
    return new BarStrategy();
  }
}

Visitor<Strategy<Base>> visitor = new MyVisitor();
Strategy<Base> strat = drawable.accept(visitor);

Base object;
strat.action(object);

Upvotes: 2

Related Questions