Map with raw generic not accepting parameter with specified generic - java

I am trying to have a map containing multiple maps. Each of those submaps contain a Set of type Domain. However, I cannot give to the super-map maps containing domains with a specific Domain (e.g.: Domain).
Domain<Spell> spells = new Domain<>("spells");
Map<String, Domain> map = new TreeMap<>();
map.put(spells.getName(), spells);
Map<String, Domain<Spell>> library = new TreeMap<>();
library.put(spells.getName(), spells);
Map<String, Map<String, Domain>> mapLibrary = new TreeMap<>();
mapLibrary.put("test", library);
However I get the following error at the last line of code:
The method put(String, Map<String,Domain>) in the type Map<String,Map<String,Domain>> is not applicable for the arguments (String, Map<String,Domain<Spell>>)
How can I do it so that I could have a super-map containing maps with multiple different Domain with different generic parameters?
Additional info: there are four classes that extend Magic:
- Spell extends Magic<Spell>
- Prayer extends Magic<Prayer>
- Mental extends Magic<Mental>
- Elemental extends Magic<Elemental>
The superclass has a generic parameter since it contains the Domain of which it is part, and as of such must specify the correct type of Domain.
Edit about the duplicate: As said above and in the comments, I seek to find a workaround to the issue, not to know why. The answers to the other question simply tell why.

The answer is Map<String, Map<String, ? extends Domain<?>>> mapLibrary = new TreeMap<>()
The error message has told you that Map<String, Domain<Spell>> is not assignable to Map<String, Domain>. This is what #john16384 commented, List<Dog> is not List<Animal>. But List<Dog> is List<? extends Animal>. So your should declare your generic as Map<String, Map<String, ? extends Domain<?>>.

Related

Passing in a value that does not match method signature

I am using Apache Ignite, which isn't really central to the question, but gives background. In that context, I've create a class extending CacheStoreAdapter that has a method with the following signature:
#Override
public void write(Entry<? extends K, ? extends V> cacheEntry) throws CacheWriterException {
I registered that class with Ignite, so that it calls the write() method whenever I give it data to save in its cache.
What I was surprised to find is that, depending on how Ignite is otherwise configured, the following code...
final V cacheObject = cacheEntry.getValue();
LOG.info("cacheObject = " + ToStringBuilder.reflectionToString(cacheObject));
... outputs the following:
cacheObject = org.apache.ignite.internal.binary.BinaryObjectImpl#7c40ffef[ctx=org.apac
That is, the cacheObject taken from an Entry<? extends K, ? extends V> is not an instance of type V!
I've worked around the issue (as I said it only happens depending on how Ignite is otherwise configured), but I am curious how this is even done in Java.
TL;DR Question:
How is is possible to pass a variable to a method that does not conform to the method's signature? Some kind of reflection technique? Is there a common / legitimate use for doing this?
In java, type parameters are optional: they are not carried along with an object instance and only exist on language level.
So you can always cast anything to and then call any methods with type checks erased:
Map<String, Integer> sim = new HashMap<>();
Map<Object, Object> oom = (Map<Object, Object>) (Map) sim;
As for BinaryObjectImpl, Ignite will try to keep objects in serialized state where possible to save on serialization costs. So you should be aware that type parameters of CacheStore are not always the user-facing types.
It is possible that the caller to your implementation of write creates an instance of the raw type Map.Entry. For example:
Entry entry = new Entry() { /* ... */ }
...
cache.write(entry);

What is the correct way to save any object as a generic Map<String, Object> in Jackson?

I'm trying to save a copy of a class in generic Map<String, Object> form for an external data type:
Map<String, Object> test1 = objectMapper.convertValue(payoutBatch,
new TypeReference<Map<String, Object>>(){});
Map<String, Object> test2 = objectMapper.convertValue(payoutItemDetails,
new TypeReference<Map<String, Object>>(){});
PayoutBatch and PayoutItemDetails are both imported from the PayPal payments Java SDK and thus can't be annotated. But trying to convert them gives:
Class com.paypal.api.payments.PayoutBatch not subtype of
[map type; class java.util.Map, [simple type, class java.lang.Object] ->
[simple type, class java.lang.Object]]
I'm still pretty new to Jackson so is this even possible? Would like to know.
I assume that given a valid Map where Object in turn could be any primitive, or List or Map you know how to serialize it to JSON using Jackson, this is trivial. So I would suggest that you create an interface (say call it Mappable) with single method:
public Map<String, Object> toMap();
And then any class that you wish to serialize to JSON using Jackson should implement this interface. Each class would then implement a method that would produce the Map that represents your class and then you can pass this Map to serialization.

JUnit assertThat: check that Object equals String

I have Map declared as following:
Map<String, Object> data
I put a String in it and verify its value like this:
assertEquals("value", data.get("key"));
Now, I'd like to rewrite the verification to use assertThat instead of assertEquals. I've tried the following:
assertThat(data.get("key"), equalTo("value"));
And of course it didn't work because of type mismatch:
Wrong 2nd argument type. Found: 'org.hamcrest.Matcher<java.lang.String>', required: 'org.hamcrest.Matcher<? super java.lang.Object>' less...
Explicit type cast of the first argument to String helps, but I'd like to avoid it. For example assertEquals doesn't require type cast.
So, how can I check that the value, which was put into Map object, declared above, is equal to particular String, using the assertThat method?
The "more assertThat" way of doing things would be:
Map<String, Object> expectedData = Collections.singletonMap("key", "value");
asssertThat(data, is(expectedData));
Please note:
Maybe you need type hints for the call to singletonMap
Besides the is matcher, there are other matchers that would allow you to check that data contains your "expected" map data
For your specific problem: that is caused because how generics come into play here; it might be sufficient to use (String) data.get("key") - to tell the compiler that the "actual" argument is of type String.
In the end - I have no idea what your problem is. I wrote down this piece of code:
public void test() {
Map<String, Object> data = new HashMap<>();
data.put("key", "value");
assertThat(data.get("key"), is("value"));
Map<String, Object> expectedData = Collections.singletonMap("key", "value");
assertThat(data, is(expectedData));
}
It compiles fine, and the unit test runs and passes. In other words: actually I am unable to repro your problem.
try this
assertThat(data.get("key"), equalTo("value"))
or
assertThat(data.get("key"), CoreMatchers.equalTo("value"))

List of a list of generic type in Java possible?

I have the following generic type:
public class Library<T> {}
I need to put each generic type into a list - for example:
ArrayList<Library<Photo>> listPhotoLibrary
= new ArrayList<Library<Photo>>();
ArrayList<Library<Video>> listVideoLibrary
= new ArrayList<Library<Video>>();
I then need to put these list into a generic list. First I tried this:
ArrayList<Library<?>> listTopLibrary = new ArrayList<Library<?>>();
The above code allowed me to add all libraries into a flat list. However, this is not what I want. What I want is to have the list of typed libraries within another list. For example, index 0 is a list of Video libraries, index 1 is a list of Photo libraries and so on. I tried the below to accomplish this:
ArrayList<ArrayList<Library<?>>> listTopLibrary
= new ArrayList<ArrayList<Library<?>>>();
This is not working. When I tried to add to the list, it is telling me:
The method add(ArrayList<Library<?>>) in the type ArrayList<ArrayList<Library<?>>>
is not applicable for the arguments (ArrayList<Library<Photo>>)
Any idea why the compiler is complaining? And if there is a way around this?
It is a compilation error because ArrayList<Library<?>> is not a supertype of ArrayList<Library<Photo>. You can declare the array like this:
ArrayList<ArrayList<? extends Library<?>>> listTopLibrary = new ArrayList<>();
A thorough explanation why can be found at Java nested generic type
You can Fix this by using
List<ArrayList<? extends Library<?>>> listTopLibrary = new ArrayList<>();
ArrayList<Library<?>> is not a supertype of ArrayList<Library<Photo>>
You have to declare listTopLibrary as
ArrayList<ArrayList<? extends Library<?>>> listTopLibrary

Java program runs fine but doesn't compile

I have a java program which runs properly.
But when I try to clean and build it in Netbeans it is choking on this line:
protected HashMap<String, ArrayList<HashMap<String,String>>> config1
config1 = new <String,ArrayList<HashMap<String,String>>> HashMap(); // build breaks here.
the error is:
cannot find symbol
symbol : constructor
<java.lang.String,java.util.ArrayList<java.util.HashMap<java.lang.String,java.lang.String>>
>HashMap()
You are placing your type parameters in wrong place. It comes in between HashMap and the (): -
config1 = new HashMap<String,ArrayList<HashMap<String,String>>>();
Also, its a good idea to have more generalized types rather than specific types in the declaration, and even in generic type parameters. So you should use Map instead of HashMap in declaration, and List instead of ArrayList in your type parameter: -
And actually, you don't need to break your declaration and initialization in two lines. Just have them in one single line. It looks more cleaner. So, you can change your two lines to: -
protected Map<String, List<Map<String,String>>> config1 =
new HashMap<String, List<Map<String,String>>>();
You have to put the class name before the generics.
config1 = new HashMap<String,ArrayList<HashMap<String,String>>>();
Generics should follow the class name. It should not be used before the class name. Correct your second line as below:
protected HashMap<String, ArrayList<HashMap<String,String>>> config1;
config1 = new HashMap <String,ArrayList<HashMap<String,String>>>();

Categories