Java Factory Pattern With Generics

17,552

Solution 1

This is a VERY GOOD question.

You could cast brutely

    return (BallUserInterface<BALL>)getBaseballUserInterface((Baseball)ball);

The answer is theoretically flawed, since we force BASEBALL=Baseball.

It works due to erasure. Actually it depends on erasure.

I hope there is a better answer that is reification safe.

Solution 2

This is a wrong design pattern. Rather than using one generic method and an if ladder, you should instead use overloading. Overloading eliminates the need for the if ladder and the compiler can make sure the correct method is invoked rather than having to wait till runtime.

eg.

public class BallUserInterfaceFactory {

    public static BallUserInterface<Baseball> getUserInterface(
            Baseball ball) {
        return new BallUserInterface<Baseball>(ball);
    }

    public static BallUserInterface<Football> getUserInterface(
            Football ball) {
        return new BallUserInterface<Football>(ball);
    }
}

This way you also get the added benefit of compile time errors if your code cannot create a BallUserInterface for the appropriate ball.


To avoid the if ladder you can use a technique known as double dispatch. In essence, we use the fact that the instance knows what class it belongs to and calls the appropriate factory method for us. For this to work Ball needs to have a method that returns the appropriate BallInterface.

You can either make the method abstract or provide a default implementation that throws an exception or returns null. Ball and Baseball should now look something like:

public abstract class Ball<T extends Ball<T>> {
    abstract BallUserInterface<T> getBallUserInterface();
}

.

public class Baseball extends Ball<Baseball> {
    @Override
    BallUserInterface<Baseball> getBallUserInterface() {
        return BallUserInterfaceFactory.getUserInterface(this);
    }
}

To make things a little neater, it's better to make getBallUserInterface package private and provide a generic getter in BallUserInterfaceFactory. The factory can then manage additional checks like for null and any thrown exceptions. eg.

public class BallUserInterfaceFactory { 
    public static BallUserInterface<Baseball> getUserInterface(
            Baseball ball) {
        return new BallUserInterface<Baseball>(ball);
    }   
    public static <T extends Ball<T>> BallUserInterface<T> getUserInterface(
            T ball) {
        return ball.getBallUserInterface();
    }
}

The Visitor Pattern

As pointed out in the comments, one problem of the above is it requires the Ball classes to have knowledge of the UI, which is highly undesirable. You can, however, use the visitor pattern, which enables you to use double dispatch, but also decouples the various Ball classes and the UI.

First, the necessary visitor classes, and factory functions:

public interface Visitor<T> {
    public T visit(Baseball ball);
    public T visit(Football ball);
}

public class BallUserInterfaceVisitor implements Visitor<BallUserInterface<? extends Ball>> {
    @Override
    public BallUserInterface<Baseball> visit(Baseball ball) {
        // Since we now know the ball type, we can call the appropriate factory function
        return BallUserInterfaceFactory.getUserInterface(ball);
    }   
    @Override
    public BallUserInterface<Football> visit(Football ball) {
        return BallUserInterfaceFactory.getUserInterface(ball);
    }
}

public class BallUserInterfaceFactory {
    public static BallUserInterface<? extends Ball> getUserInterface(Ball ball) {
        return ball.accept(new BallUserInterfaceVisitor());
    }
    // other factory functions for when concrete ball type is known
}

You'll note that the visitor and the factory function have to use wildcards. This is necessary for type safety. Since you don't know what type of ball has been passed, the method cannot be sure of what UI is being returned (other than it is a ball UI).

Secondly, you need to define an abstract accept method on Ball that accepts a Visitor. Each concrete implementation of Ball must also implement this method for the visitor pattern to work correctly. The implementation looks exactly the same, but the type system ensures dispatch of the appropriate methods.

public interface Ball {
    public <T> T accept(Visitor<T> visitor);
}

public class Baseball implements Ball {
    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

Finally, a bit of code that can put all this together:

Ball baseball = new Baseball();
Ball football = new Football();

List<BallUserInterface<? extends Ball>> uiList = new ArrayList<>();

uiList.add(BallUserInterfaceFactory.getUserInterface(baseball));
uiList.add(BallUserInterfaceFactory.getUserInterface(football));

for (BallUserInterface<? extends Ball> ui : uiList) {
    System.out.println(ui);
}

// Outputs:
// ui.BaseballUserInterface@37e247e2
// ui.FootballUserInterface@1f2f0ce9
Share:
17,552

Related videos on Youtube

FuryComputers
Author by

FuryComputers

Updated on June 04, 2020

Comments

  • FuryComputers
    FuryComputers about 4 years

    I would like my BallUserInterfaceFactory to return an instance of a user interface that has the proper generic type. I am stuck in the example below getting the error:

    Bound mismatch: The generic method getBaseballUserInterface(BASEBALL) of type BallUserInterfaceFactory is not applicable for the arguments (BALL). The inferred type BALL is not a valid substitute for the bounded parameter

    public class BallUserInterfaceFactory {
        public static <BALL extends Ball> BallUserInterface<BALL> getUserInterface(BALL ball) {
    
            if(ball instanceof Baseball){
                return getBaseballUserInterface(ball);
            }
            //Other ball types go here
    
            //Unable to create a UI for ball
            return null;
        }
    
        private static <BASEBALL extends Baseball> BaseballUserInterface<BASEBALL> getBaseballUserInterface(BASEBALL ball){
            return new BaseballUserInterface<BASEBALL>(ball);
        }
    }
    

    I understand that it cannot guarantee that BALL is a Baseball, and so there is a parameter type mismatch on the getBaseballUserInterface method call.

    If I cast the ball parameter in the getBaseballUserInterface method call, then I get the error:

    Type mismatch: cannot convert from BaseballUserInterface<Baseball> to BallUserInterface<BALL>

    Because it can't guarantee that what I am returning is the same type of BALL.

    My question is, what is the strategy for dealing with this situation?

    (For completeness, here are the other classes required in the example)

    public class Ball {
    
    }
    
    public class Baseball extends Ball {
    
    }
    
    public class BallUserInterface <BALL extends Ball> {
    
        private BALL ball;
    
        public BallUserInterface(BALL ball){
            this.ball = ball;
        }
    }
    
    public class BaseballUserInterface<BASEBALL extends Baseball> extends BallUserInterface<BASEBALL>{
    
        public BaseballUserInterface(BASEBALL ball) {
            super(ball);
        }
    
    }
    
    • ollins
      ollins almost 12 years
      Is BaseballUserInterface extends BallUserInterface<BaseBall> not enough? Do you have Subclasses of BaseballUserInterface?
    • FuryComputers
      FuryComputers almost 12 years
      @ollins I was using that strategy originally, if you change the example to that, you will end up with another "Type mismatch" situation because there is no guarantee that the BALL type returned from the factory is the same as the BALL type in the BallUserInterface. (This time specified in the BaseballUserInterface class declaration).
  • artbristol
    artbristol almost 12 years
    IMHO that cast is safe, despite the warning. With reification, wouldn't the cast be checked at runtime and presumably always work?
  • FuryComputers
    FuryComputers almost 12 years
    I really like this answer, but here is the issue I have with it: If I have a Ball then I cant call getUserInterface without putting an if ladder somewhere else. For instance: Ball ball = new Baseball(); BallUserInterface<?> ui = BallUserInterfaceFactory.getUserInterface(ball); Does not work because there could be other types of Ball that are not overloaded in the factory method.
  • artbristol
    artbristol almost 12 years
    @FuryComptuers But if you only have a Ball, you don't need the additional information a generic method will give you. You can just receive BallUserInterface<?>.
  • Dunes
    Dunes almost 12 years
    I added a bit about using double dispatch to avoid if ladders.
  • HeavyE
    HeavyE almost 12 years
    An object should not have any information about its User Interface, so adding a UI return method to the object is not the correct solution.
  • HeavyE
    HeavyE almost 12 years
    I should also add that it isn't even reasonable to assume that someone building a UI for a Ball object has the ability to change the Ball object. (though a wrapper could be created client-side)
  • FuryComputers
    FuryComputers over 11 years
    I have up voted this answer because think it may help others, but for me, it just moved the problem to other places in the code. I also agree with HeavyE that the object should not have any knowledge of it's UI.
  • FuryComputers
    FuryComputers over 11 years
    It is unfortunate, but this seems to be the only solution that doesn't move the problem somewhere else in the code.