Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance doesn't work with passed as generic type

Tags:

java

oop

generics

Consider my custom extended hashmap:

public class CustomHashMap extends HashMap<String, Object> {
...
}

Why doesn't this work since CustomHashMap is child of HashMap?

Map<String, HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();

But this works:

Map<String, HashMap<String, Object>> customs = new LinkedHashMap();

And also it works when adding (put) an CustomHashMap into the customs Map.

customs.put("test", new CustomHashMap());

It seems weird that not specifying the generics at initialization works, but it doesn't otherwise.

like image 955
user1236048 Avatar asked Nov 14 '13 11:11

user1236048


People also ask

Is it possible to inherit from a generic type?

You can't inherit from a Generic type argument. C# is strictly typed language. All types and inheritance hierarchy must be known at compile time. . Net generics are way different from C++ templates.

Can generics be used with inheritance in several ways what are they?

Generics also provide type safety (ensuring that an operation is being performed on the right type of data before executing that operation). Hierarchical classifications are allowed by Inheritance. Superclass is a class that is inherited. The subclass is a class that does inherit.

Can a non generic class inherit a generic class?

Yes you can do it.

Which reference types Cannot be generic?

Almost all reference types can be generic. This includes classes, interfaces, nested (static) classes, nested interfaces, inner (non-static) classes, and local classes. The following types cannot be generic: Anonymous inner classes .


2 Answers

This statement is not working

Map<String, HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();

because customs is of type Map<String, HashMap<String, Object>> and you are assigning a LinkedHashMap which is of type <String, CustomHashMap>, where CustomHashMap is a sub class of HashMap<String, Object>.

Generics are invariant: for any two distinct types T1 and T2, HashMap<String, T1> is neither a subtype nor a supertype of HashMap<String, T2>. So, LinkedHashMap<String, CustomHashMap> cannot be assigned to Map<String, HashMap<String, Object>>. On the other hand, arrays are covariant, which means below statement will compile without any error or warning. But, it might fail at run time (which might cause more harm) if you put any other subtype of HashMap<String, Object> into it other than CustomHashMap :

HashMap<String, Object>[] mapArray = new CustomHashMap[1];
mapArray[0] = new CustomHashMap_1();// this will throw java.lang.ArrayStoreException

Now, if you want to assign LinkedHashMap<String, CustomHashMap> to Map<String, HashMap<String, Object>> , change the statement to this:

Map<String, ? extends HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();

Some additional information about this approach is nicely explained by @Seelenvirtuose , which is the accepted answer.

like image 186
Debojit Saikia Avatar answered Sep 24 '22 05:09

Debojit Saikia


When working with generics, you should always keep type erasure in mind. At runtime an objct of type Map does not know its type parameters anymore. The consequence: A LinkedHashMap<String, CustomHashMap> is not a sub-type of Map<String, HashMap<String, Object>>.

If you want to have somthing sub-type related you must do it the following way:

Map<String, ? extends HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();

This is called an upper-bounded wildcard and exists exactly for that case: To get a sub-type relationship. Please refer to the Java tutorial about generics for more information.


An additional info as per the comment:

The upper-bounded version has a disadvantage on how to use the customs map. You cannot put instances anymore into that map. The only value allowed is null. The reason is, that you could have another class extending Map<String, HashMap> and try to put an instance of that into your customs map. But this is a problem, as the variable customs refers to a map that was parameterized with CustomHashMap.

When working with bounded wildcards, you should always remind PECS. PECS stands for "producer extends, consumer super". This is valuable for method parameters. If you write a method that only needs to read values from such a map, you could type the parameter as Map<String, ? extends Map<String, Object>>. This is called a producer. If you only need to write to that map, use the keyword super. If you need both - read and write - you cannot do either.

like image 38
Seelenvirtuose Avatar answered Sep 22 '22 05:09

Seelenvirtuose