Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert implementors on one interface into another?

Tags:

java

generics

I'm having trouble finding a way to do this in a way that doesn't seem wrong, given the following

public interface IType {}
public interface IMode {}

public interface Factory<T extends IType> {
   IMode get(T o);

   Class<T> getIType();
} 

I have the above interfaces and a large list on classes the implement both IType and IMode with corresponding factories.

I need to be able to convert from one to the other, for example,

public class A implements IType {}
public class One implements IMode {}

public class AToOne implements Factory<A> {
    public IMode get(A o){
       return new One();
    }

    public Class<A> getIType(){
       return A.class;
    }
}

Given that there is a 1 to 1 mapping of these classes, ie for every concrete IType there is one and only one concrete IMode with corresponding factory, how would I go about converting a list of ITypes to a list of IModes?

ie.

private List<Factory<? extends IType>> factoryList;

public List<IMode> getConversions(List<? extends IType> types){
    ???
}

My first try did not go so well,

//Fill this using the getIType() method from each factory
Map<Class<IType>, Factory<? extends IType>> factoryList = new HashMap<Class<IType>, Factory<? extends IType>>();

 public List<IMode> getConversions(List<IType> types){
    List<IMode> modes = new ArrayList<IMode>();

    for(IType type : types){
        //Derp
        Factory<? extends IType> factory = factoryList.get(type.getClass());
        //Error
        factory.get(factory.getIType().cast(type));
    }
}

Error:

 The method get(capture#12-of ? extends IType) in the type
 Factory<capture#12-of ? extends IType>
 is not applicable for the arguments (capture#14-of ? extends IType)
like image 790
Andrew Avatar asked Dec 29 '25 21:12

Andrew


2 Answers

Like I mentioned in my comment, you just need to use a generic helper method to access the map, which performs an unchecked cast from Factory<? extends IType> to a Factory<T> where T matches the type of what's passed in:

Map<Class<? extends IType>, Factory<? extends IType>> factoryList =
        new HashMap<Class<? extends IType>, Factory<? extends IType>>();

private <T extends IType> IMode convert(T iType) {
    //unchecked cast - implementation must guarantee map holds correct data
    Factory<T> factory = (Factory<T>)factoryList.get(iType.getClass());
    //then convert
    return factory.get(iType);
}

You can call this helper method from the loop:

public List<IMode> getConversions(List<IType> types) {
    List<IMode> modes = new ArrayList<IMode>(types.size());
    for (IType type : types) {
        IMode iMode = convert(type);
        modes.add(iMode);
    }
    return modes;
}
like image 154
Paul Bellora Avatar answered Dec 31 '25 09:12

Paul Bellora


The simple solution is the following:

interface IFoo {
}

interface IBar {
}

private static class Foo implements IFoo {
}

private static class Bar implements IBar {
}

interface IFoo2IBarConverter<B extends IBar, F extends IFoo> {
    B convert(F foo);
}

private static class Foo2BarConverter implements IFoo2IBarConverter<Bar, Foo> {
    public Bar convert(Foo foo) {
        return new Bar();
    }
}

private static class IFoo2IBarFactory {
    private static HashMap<Class<? extends IFoo>, IFoo2IBarConverter<? extends IBar, ? extends IFoo>> converters = new HashMap<>();

    static {
        converters.put(Foo.class, new Foo2BarConverter());
    }

    public static<F extends IFoo, B extends IBar> B convert(F foo) {
        // ugly unchecked cast here
        IFoo2IBarConverter<B, F> converter = (IFoo2IBarConverter<B, F>) converters.get(foo.getClass());
        return converter.convert(foo);
    }
}


public static void main(String[] args) {
    Foo foo = new Foo();
    IBar bar = IFoo2IBarFactory.convert(foo);
}

You just take a HashMap that maps a specific class that's a subtype of IFoo to some converter interface. The converter takes the IFoo instance and converts it into a IBar.. actually into the specific classes we want. Sadly we get an ugly cast in IFoo2IBarFactory.convert() and I don't think there's any way to avoid that one. Still at least it's only in one localized position and with the right comment and a SuppressWarning you can live with it, I'd think

like image 22
Voo Avatar answered Dec 31 '25 09:12

Voo