Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics Type Safety warning with recursive Hashmap

I'm using a recursive tree of hashmaps, specifically Hashmap map where Object is a reference to another Hashmap and so on. This will be passed around a recursive algorithm:

foo(String filename, Hashmap<String, Object> map)
{
    //some stuff here
    for (Entry<String, Object> entry : map.entrySet()) 
    {
       //type warning that must be suppressed
       foo(entry.getKey(), (HashMap<String, Object>)entry.getValue());
    }
}

I know for sure Object is of type Hashmap<String, Object> but am irritated that I have to suppress the warning using @SuppressWarnings("unchecked").

I'll be satisfied with a solution that does either a assert(/*entry.getValue() is of type HashMap<String, Object>*/) or throws an exception when it isn't. I went down the Generics route for compile type safety and if I suppress the warning then it defeats the purpose.

Thank you for your comments, ksb

like image 396
GC. Avatar asked Mar 14 '10 21:03

GC.


People also ask

How does generics ensure type safety?

Generics were added to Java to ensure type safety. And to ensure that generics won't cause overhead at runtime, the compiler applies a process called type erasure on generics at compile time. Type erasure removes all type parameters and replaces them with their bounds or with Object if the type parameter is unbounded.

What is recursive generics in Java?

We can use generics while defining our builders to tell Java that return type of methods is not the builder's class but rather the subclass of the builder, hence recursive generic definition.

What is generics in Java with simple example?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.

How generics works internally in Java?

Java generics are implemented through type erasure, i.e. type arguments are only used for compilation and linking, but erased for execution. That is, there is no 1:1 correspondence between compile time types and runtime types.


1 Answers

This is possible using a generic method with a recursive type variable. Try the following:

public <T extends Map<String, T>> void foo(String filename, T map) {
    //some stuff here
    for (Map.Entry<String, T> entry : map.entrySet())  {
        foo(entry.getKey(), entry.getValue());
    }
}

Should compile fine without any warnings.

However, if you have control of the map, and can substitute your own class, it might be more readable to make a class Node (this looks like a tree to me), that contains a Map instead. Something like:

public class Node {
    private Map<String, Node> children;

    ...
    // accessor methods to retrieve children ...
}

And have foo take a Node as its second argument instead. Just a suggestion.

like image 50
waxwing Avatar answered Sep 25 '22 21:09

waxwing