简体   繁体   中英

Java: Working with Generics and Maps without Casting / @SuppressWarnings

I now came several times across this problem and always kinda solved this with some casts and @SuppressWarnings annotations.

The relevant interfaces / abstract classes:

public abstract class Data { }

public interface DataOperations {
    boolean isValid();
}

public interface DataOperationsFactory<T extends Data> {
    Class<T> getDataClass();
    DataOperations getOperations(T data);
}

Example Implementations:

public class DataImpl1 extends Data {
    public String foo;
}

public class DataImpl1Operations implements DataOperations {
    private DataImpl1 data;
    public DataImpl1Operations(DataImpl1 data) {
        this.data = data;
    }
    public boolean isValid() {
        return data.foo != null;
    }
}

public class DataImpl1OperationsFactory extends DataOperationsFactory<DataImpl1> {
    public Class<DataImpl1> getDataClass() {
        return DataImpl1.class;
    }
    DataOperations getOperations(DataImpl1 data) {
        return new DataImpl1Operations(data);
    }
}

Using this pattern, I can decide whether I need to create a new DataImpl1Operations everytime. Or maybe use a final static NO_OP implementation or what have you.

The Code:

Now I'd like to put all those factories inside a Map<Class<T>, DataOperationsFactory<T>> (constructor). And afterwards read from it ( getOps method).

public class Test {
    Map<Class<?>, DataOperationsFactory<?>> map;

    public Test(List<DataOperationsFactory<?>> fs) {
        for(DataOperationsFactory<?> f : fs) {
            map.put(f.getDataClass(), f);
        }
    }

    @SuppressWarnings("unchecked")
    public <T extends Data> DataOperations getOps(T data) {
        // --> Here I need to do an unchecked cast <--
        DataOperationsFactory<? super T> f =
                (DataOperationsFactory<? super T>) map.get(data.getClass());
        return f.getOperations(data);
    }
}

Is there any way doing this without unchecked casting?

You can delegate to a private method that captures the type, so it can be used to reliably cast to the correct Data subclass:

Map<Class<?>, DataOperationsFactory<?>> map;

// Unchanged
public Test(List<DataOperationsFactory<?>> fs) {
    for(DataOperationsFactory<?> f : fs) {
        map.put(f.getDataClass(), f);
    }
}

public DataOperations getOps(Data data) {
    DataOperationsFactory<?> f = map.get(data.getClass());
    return getOperations(f, data);
}

private static <T extends Data> DataOperations getOperations(DataOperationsFactory<T> f,
                                                             Data data) {
    return f.getOperations(f.getDataClass().cast(data));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM