I searched and tried now for more than a day and could not find a solution for a common problem I have in Java. The reason is obvious - Type erasure. But the question I have is: Is there really no good solution for this problem in Java? I am willing to investigate even more time since this kind of problem pops up every once in a time.
The error I get is:
The method doStrategy(capture#2-of ? extends I) in the type IStrategy is not applicable for the arguments (I)
So I simplified the problem to the following example.
Imagine the model:
package model;
public interface I {
//there are actual 30 classes implementing I...
}
public class A implements I {
public void someSpecificMagicForA(){
System.out.println("A");
}
}
public class B implements I {
public void someSpecificMagicForB() {
System.out.println("B");
}
}
and the selection logic
package strategy;
import model.A;
public interface IStrategy<T> {
public void doStrategy(T t);
}
public class AStrategy implements IStrategy<A> {
@Override
public void doStrategy(A a) {
a.someSpecificMagicForA();
}
}
public class BStrategy implements IStrategy<B> {
@Override
public void doStrategy(B b) {
b.someSpecificMagicForB();
}
}
and a generic strategy factory
package strategy;
import java.util.HashMap;
import java.util.Map;
import model.A;
import model.B;
public class StrategyFactory {
static {
strategies.put(A.class, AStrategy.class);
strategies.put(B.class, BStrategy.class);
}
private static final Map<Class<?>, Class<? extends IStrategy<?>>> strategies = new HashMap<>();
@SuppressWarnings("unchecked") // I am fine with that suppress warning
public <T> IStrategy<T> createStategy(Class<T> clazz){
Class<? extends IStrategy<?>> strategyClass = strategies.get(clazz);
assert(strategyClass != null);
try {
return (IStrategy<T>) strategyClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
And here is the test
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import model.A;
import model.B;
import model.I;
import strategy.IStrategy;
import strategy.StrategyFactory;
public class TestCases extends TestCase {
public void testWithConcreteType(){
B b = new B();
StrategyFactory factory = new StrategyFactory();
IStrategy<B> createStategy = factory.createStategy(B.class);
createStategy.doStrategy(b); //awesome
}
public void testWithGenericType(){
List<I> instances = createTestData(); // image this is the business data
StrategyFactory factory = new StrategyFactory();
for (I current : instances){
IStrategy<? extends I> createStategy = factory.createStategy(current.getClass());
createStategy.doStrategy(current); //meh
//The method doStrategy(capture#2-of ? extends I) in the type IStrategy<capture#2-of ? extends I>
//is not applicable for the arguments (I)
}
}
private List<I> createTestData(){
A a = new A();
B b = new B();
List<I> instances = new ArrayList<>();
instances.add(a);
instances.add(b);
return instances;
}
}
I have tried another approach using guava TypeTokens (http://ift.tt/1iFUmjw). But I did not manage to get this working since I really have no since all I get is that Collection of instances implementing that interface.
I have a working and not so bad solution though using the Visitor-pattern. Since I there have the real classes
...visitor class
public void visit(A a){
doVisit(A.class, a); //private generic method now works of course
}
everything is fine again at compile time. But in this particular case it took me quite some time to implement that visitor for more than 30 sub-classes of I. So I would really like to have a better solution for the future.
Any comments are much appreciated.
Aucun commentaire:
Enregistrer un commentaire