I have an interface to convert object to string:
public interface Converter<T> {
String asString(T object);
}
And a map to store all available converters:
Map<Class<?>, Converter<?>> converterMap;
Now I have a list of heterogeneous data to convert like this:
List<?> data = fetchData();
List<String> stringData = new ArrayList<>(data.size());
for (Object datum : data) {
stringData.add(convertrMap.get(datum.getClass()).asString(datum));
}
But this code doesn't compile:
error: method asString in interface Converter<T> cannot be applied to given types;
stringData.add(converterMap.get(datum.getClass()).asString(datum));
required: CAP#1
found: Object
reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion
where T is a type-variable:
T extends Object declared in interface Converter
where CAP#1 is a fresh type-variable:
CAP#1 extends Object from capture of ?
How should I change the code?
You are facing issue called wildcard capture. Java is unable to identify the type that will be received from the
List<?>
data. Try refactoring your code in any of the two waysMethod 1: Change your interface as below
Method 2: Helper method to capture wild card by type inference
Create an helper method as below,
Call this helper method as below
First, you should encapsulate the map inside a helper class like this, whose operations preserve the invariant (that
Class<T>
maps toConverter<T>
):Now, to break down the task, let's take the small step of writing a function that takes any object and converts it based on the converter map (assuming the object's class is in the converter map):
This seems simple, but is harder than it looks, because you will run into a special case of the Java type system in how it types
.getClass()
. You will have the problem of convincing the compiler thatx
is an instance of the parameter ofx.getClass()
. The best way to solve this is:And then you can solve the rest of the problem:
You should change the code like this:
I think it will work.