I have a couple of classes having identical methods, except with respect to certain parameter types:
interface ICls<T> { void doSomething(String key, T value); Map<String, T> getSomething(); } class ClsA implements ICls<Boolean> { @Override public void doSomething(String key, Boolean value) { } @Override public Map<String, Boolean> getSomething() { return Map.of(); } } class ClsB implements ICls<String> { @Override public void doSomething(String key, String value) {} @Override public Map<String, String> getSomething() { return Map.of(); } }
Now I’m trying to have a main class that stores a mixed list of these class objects and for each of these instances, passes info between its two methods:
class Main { List<ICls<?>> list = List.of( new ClsA(), new ClsB() ); void run() { list.forEach(cls -> { Map<String, ?> data = cls.getSomething(); data.keySet().forEach(key -> cls.doSomething(key, data.get(key))); }); }
The List<ICls<?>>
and Map<String, ?>
statements are OK. However, the map.get(key)
throws an IDE error:
'doSomething(<String, capture<?>>) in '...ICls' cannot be applied to 'String, capture<?>'
Hovering the mouse cursor over the offending statement shows:
Required type: capture of ? Provided: capture of ?
Assuming that I can’t/don’t want to change the generic type T
to Object
, and don’t want to change the architecture either, what can I do to make the code here compile?
I’ve tried changing the signature of doSomething
so that it accepts the entire Map<String, T>
and call it like so, with no luck either:
cls.doSomething(cls.getSomething());
Advertisement
Answer
This compiles for me:
import java.util.List; import java.util.Map; public class Comparison { interface ICls<T> { void doSomething(String key, T value); Map<String, T> getSomething(); } static class ClsA implements ICls<Boolean> { public void doSomething(String key, Boolean value) {} public Map<String, Boolean> getSomething() { return null; } } static class ClsB implements ICls<String> { public void doSomething(String key, String value) {} public Map<String, String> getSomething() { return null; } } static class Main { List<ICls<?>> list = List.of( new ClsA(), new ClsB() ); void run() { list.forEach(cls -> { doIt(cls); }); } <T> void doIt(ICls<T> cls) { Map<String, T> data = cls.getSomething(); data.keySet().forEach(key -> cls.doSomething(key, data.get(key))); } } }
It makes clear the relationship between the map and the cls.
In the original context, because the type of the List is ICls<?> we can’t get that relationship, but once we get a single ICls we can introduce a type variable T
which allows us to express the relationship between getSomething
and doSomething
.