bugger
bugger

Reputation: 45

Java: make one of the Map<> variable generic

I need to store variable and value as a pair somewhere and I tend to use Map. The problem is the type of value would be different so I think generic should be used. So I tried something like this:

Map<String, T> netPara;

and of course it does not work, then I tried

Map<String, Object> netPara;

This worked but when I retrived value it returned only of type Object, instead of "boolean", "int".

Can anyone help me out? I believe it tends to be simple but I am not an expert, yet. Thanks in advance.

In fact I have a function A who receives a list of parameters, but then it's function B who will also use these parameters but B's parameters list is vide, so I hope to store them as a class parameter and reused by function B. So here is what I did:

Class Person() {
Map<String, T> infoPaire; // if I use Object to replace T
Function A: receiver(boolean b, int i, String1 st1, String2 st2) {
    infoPaire.put("b", b);
    infoPaire.put("i", i);
    infoPaire.put("st1", st1);
    infoPaire.put("st2", st2);
}

Function B: user() {
    boolean b = infoPaire.get("b"); // I need to add (boolean) to cast and I dont want it, it's ugly
    int i = infoPaire.get("i");
    String st1 = infoPaire.get("st1");
    String st2 = infoPaire.get("st2");
}

Upvotes: 1

Views: 2589

Answers (7)

Muneeb Nasir
Muneeb Nasir

Reputation: 2504

You need to understand generic and type-casting first. As far as your question is concerned, Object Class stood on top level in inheritance, means Object is the parent class of All java classes.

So you can cast any object in into Object class with out casting, but when you need to get you object back in its actual class you have to cast it. like String str = (String) netPara.get(key);

you need to look into polymorphism, casting, and generic concepts. Generic come to solve the problem you are facing right now.

Upvotes: 0

isomarcte
isomarcte

Reputation: 1981

I see now that you are asking a slightly different question than I had originally thought. I will leave my other answer up though, since it explains why this is a problem.

Since you are only dealing with Strings, Booleans, and Integers you could do what you are asking by only storing Strings in your Map and converting to the proper type later.

Map<String, String> m = new HashMap<>();
m.put("bool", "true");
m.put("int", "1");

Integer i = Integer(m.get("int"));
Boolean b = Boolean(m.get("bool"));

This would be pseudo safe, but you would have to track the types for the values somewhere else in your code.

Probably the best solution would be to use three Maps, one for each type.

Map<String, String> stringMap;
Map<String, Integer> intMap;
Map<String, Boolean> boolMap;

There is really no harm in having three maps, in general.

For reasons why you can't use one map cleanly, see my other answer on this question.

Upvotes: 1

isomarcte
isomarcte

Reputation: 1981

T needs to be defined as a generic parameter on either the class or the method.

Method

public <T> Map<String, T> getMap(){
    return new HashMap<String, T>();
}

Class

public class Test <T> {

    public Map<String, T> getMap(){
        return new HashMap<String, T>();
     }
}

Usage

Method

When using the method

Test t = new Test();
Map<String, Double> m = t.<Double>getMap();

or more concisely,

Map<String, Double> m = t.getMap();

Since the Double can be inferred from the right hand side.

Class

With a class.

Test<String> t = new Test<String>;

or with type inference,

Test<String> t = new Test<>;

And

Map<String, Integer> m = t.getMap()

And all together,

Test<String> t = new Test<>;
Map<String, String> m = t.getMap();
Test<Integer> t2 = new Test<>;
Map<String, Integer> m2 = t2.getMap();

Warning

For the type inference stuff, i.e. <> and t.getMap() with no type parameter, you must be using Java 7 or greater.

Other Warning About the Type System in General

Using generics allows you to create code that manipulates values without knowing what they are, but there are some caveats. With reference to your question, it sounds like you might be trying to store two values of different type in the Map, like so,

m.put('int', 7)
m.put('bool', true)

This would not be possible. Because T, while generic, must eventually resolve to a single type for instance of the class. For instance,

Map<String, Integer> m;

This map could only ever hold Integer values.

The reason that declaring Object as the value works is because every class in Java is a subclass of Object, so anything can go in there. But using Object is very bad design. You are essentially turning off the type system, which is one of the major advantages that a language like Java brings to the table, static typing.

So, if you find yourself wanting to store a collection of homogeneous values, you might want to reconsider your design. Even in languages which explicitly allow such things (scheme, python, JavaScript, etc.) it is usually a sign of a design flaw to use them in a homogeneous manner.

Upvotes: 3

Joop Eggen
Joop Eggen

Reputation: 109547

You want something like

BigDecimal n = dictionary.get("n");
String s = dictionary.get("s");

which could have

class Dictionary {
    public <T> T get(String name) { ... }

Because of the type erasure (the compiled/run-time code no longer knowing the generic types), you cannot do this, cast the object to return as T.

The solution:

BigDecimal n = dictionary.get(BigDecimal.class, "n");
String s = dictionary.get(String.class, "s");

which could be implemented with run-time cast exceptions:

public class Dictionary {
    private Map<String, Object> map = new HashMap<>();

    public <S> S put(Class<S> type, String name, S value) {
        Object oldValue = map.put(name, value);
        return type.cast(oldValue);
    }

    public <T> T get(Class<T> type, String name) {
        Object value = map.get(name);
        return type.cast(value);
    }
}

One could store the type itself, maybe as additional key, a Map<Class<?>, Map<String, Object>>>, but one still would not have gained much in this too open problem context.

Upvotes: 0

SureshBonam
SureshBonam

Reputation: 196

Following is the way to put elements into the map check it once

 public class TakingInput {

public static void main(String[] args) {

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

Integer in=new Integer(123);

Object obj=in;

String s=new String("suresh");

Object obj2=s;

map.put("hi",obj);

map.put("ramesh",obj2);

System.out.println(map);

  //to retrive 

 String ss=(String) map.get("ramesh"); //type casting

System.out.println(ss); //o/p:suresh


}

 }

Upvotes: 0

Bohemian
Bohemian

Reputation: 424993

The best you can hope for is a type of Object for value and a typed getter method that has an unsafe cast:

class VariableMap extends HashMap<String, Object> {
   public <T> T getValue(Object key) {
       return (T)super.get(key);
   }
{

Upvotes: 0

Jesper
Jesper

Reputation: 206786

You can make one of the types a type parameter, but then you must also declare it as a type parameter. Just giving it a one-letter name such as T does not automatically make it a type parameter.

class MyClass<T> {   // Note: T is a type parameter
    private Map<String, T> netPara;

    // ...
}

Upvotes: 0

Related Questions