I have a variable in the object:
private final Map<Long, ? extends MovieInfoDTO> bElementsToAdd = new HashMap<>();
Using the method in the Builder pattern I want to complete this map.
public Builder withElementsToAdd(@Nullable final Map<Long, ? extends MovieInfoDTO> elementsToAdd) {
this.bElementsToAdd.clear();
if(elementsToAdd != null) {
this.bElementsToAdd.putAll(elementsToAdd);
}
return this;
}
However, I receive an error.
putAll
(java.util.Map<? extends java.lang.Long,? extends capture<? extends com.jonki.popcorn.common.dto.movie.MovieInfoDTO>>)
in Map cannot be applied
to
(java.util.Map<java.lang.Long,capture<? extends com.jonki.popcorn.common.dto.movie.MovieInfoDTO>>)
How do I correct this method of supplementing the map?
Short answer: you can't add values to a map with that type. As a rule of thumb, you can read ? extends
as "read-only", so your map is a map of "read-only" MovieInfoDTO
objects.
Longer answer: ? extends T
is a bit of a weird type; as you're running up against, the type checker considers different syntactic occurrences expressions with type ? extends T
to be different, incompatible "captures" that don't type check as equal to each other. So since bElementsToAdd
has type Map<Long, ? extends MovieInfoDTO>
, then bElementsToAdd.put()
takes a value of a new "capture" of ? extends MovieInfoDTO
. But there's no way to get that same capture! Even if you did bElementsToAdd.put(k, bElementsToAdd.get(k))
, the put
and get
use different captures so they aren't compatible types!
You clearly don't want a read-only map since you're trying to update it. So, you have a few options. One is to remove the wildcard -- that makes sense if you don't care what subtype of MovieInfoDTO
you've got and you'd be fine with mixing them in the same map. If you do care, then you probably meant to add a bound to your overall map:
class MyClass<T extends MovieInfoDTO> {
// ...
private final Map<Long, T> bElementsToAdd = new HashMap<>();
//...
public Builder withElementsToAdd(@Nullable final Map<Long, T> elementsToAdd) {
// ...
}
This enforces the constraint that the type of elements you add in withElementsToAdd
must have the same type as the ones in the map, even if you don't care exactly what type that is.
When you place an upper-bound wildcard in your declaration of bElementsToAdd
, the compiler doesn't know which subtype of MovieInfoDTO
you have there. You can theoretically and legally assign a Map<Long, MovieInfoSubclassDTO>
(MovieInfoSubclassDTO
being a theoretical subclass of MovieInfoDTO
) to bElementsToAdd
.
The method putAll
is supposed to be able to place all mappings from another map into the original map. It could have a type as high up as Map<Long, MovieInfoDTO>
. The variable bElementsToAdd
must be able to accept MovieInfoDTO
s as keys.
Solution: Remove the wildcard upper-bound on bElementsToAdd
.
private final Map<Long, MovieInfoDTO> bElementsToAdd = new HashMap<>();
You do not need to remove the upper-bound wildcard on elementsToAdd
(the parameter to withElementsToAdd
), and you should not remove it. Retaining the upper-bound wildcard here allows calling code to pass in a Map<Long, MovieInfoSubclassDTO>
, which is acceptable because the type MovieInfoSubclassDTO
, the theoretical subclass of MovieInfoDTO
, should be allowed as values in bElementsToAdd
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With