black_belt
black_belt

Reputation: 6799

getter setter for Hashmap using generics

I have the following two classes

Animal Class

class Animal {

  Map<String, A> data = new HashMap <String, A>();

  public void setValue(HashMap<String, ?> val)
   {
     this.data = val;
   }
  public Map getValue()
   {
    return this.data;
   }
}

Dog Class

class Dog extends Animal {

  public void index()
  { 
    Map<String, A> map = new HashMap<String, A>();

    map.put("name", "Tommy");
    map.put("favfood", "milk"); // want to pass Lists, Integers also
    setValue(map);
  }
}

As you can see from the above code I am trying to set some values with keys in index method, but I am getting error warnings from eclipse in both two files. Error Messages are:

In Dog Class File:

Multiple markers at this line
    - A cannot be resolved 
     to a type
    - A cannot be resolved 
     to a type

In Animal Class File :

Multiple markers at this line
    - A cannot be resolved to a type
    - A cannot be resolved to a type
    - Incorrect number of arguments for type Map<K,V>; it cannot be parameterized with arguments 
     <HashMap<String,A>>

The data type of the keys in HashMap will always be a String but the data types of values would be random, hence I am trying to use Generics.

Coming from PHP background I still haven't been able to grasp the concept of Java Generics. Could you please tell me where is the mistake in my code?

Upvotes: 0

Views: 28697

Answers (6)

mi_mo
mi_mo

Reputation: 184

Keep in mind that you shouldn't use the mix of Generic type and RAW type same . a easiest way as my opinion is use Object instead A :

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

Upvotes: 0

Chris Franklin
Chris Franklin

Reputation: 167

While this kind of setup is not the ideal way to go, one solution is to make your Map a <String, Object> generic type. In this way you can put whatever you want into the Object part. This will, however, be a pain to pull that information back out. This is how I see your classes changing.

class Animal {

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

      public void setValue(Map<String, Object> map)
       {
         this.data = map;
       }
      public Map<String, Object> getValue()
       {
        return this.data;
       }
    }

class Dog extends Animal {

      public void index()
      { 
        Map<String, Object> map = new HashMap<String, Object>();

        map.put("name", "Tommy");
        map.put("favfood", "milk"); // want to pass Lists, Integers also
        setValue(map);
      }
    }

Upvotes: 2

PeterK
PeterK

Reputation: 1723

The problem is that you can't just introduce a type parameter in Java generics and expect it to work. For example looking at the Animal class:

class Animal {

  Map<String, A> data = new HashMap <String, A>();

  public void setValue(HashMap<String, ?> val)
   {
     this.data = val;
   }
  public Map getValue()
   {
    return this.data;
   }
}

Type parameter A is not defined, so in this case, the compiler will look for a class name A. You can solve that by introducing a type parameter for the class:

class Animal<A> { // <--- <A> introduces A as a type parameter

  Map<String, A> data = new HashMap <String, A>();

  public void setValue(HashMap<String, ?> val)
   {
     this.data = val;
   }
  public Map getValue()
   {
    return this.data;
   }
}

However the problem with this is, that you then need to define A when you initialise a new Animal for example:

Animal<String> dog = new Animal<>();
dog.setValue("name", "max");  //<---- ok
dog.setValue("age", 13);      //<---- won't compile

What this means however, is that your data Map can only map from String to String, you will not be able to store integers.

You could get around this by using wildcards, like this for example:

class Animal {

  Map<String, ?> data = new HashMap <String, A>(); 

  public void setValue(HashMap<String, ?> val)
   {
     this.data = val;
   }
  public Map getValue()
   {
    return this.data;
   }
}

Now you can store every data type derived from Object in your map, a String or even an Integer.

Animal dog = new Animal();
dog.setValue("name", "max");  //<---- ok
dog.setValue("age", 13);      //<---- ok

The problem with this solution is that you are only to retrieve the elements in this map in a type save manner.

dog.getValue().get("name")  //<---- ok 
Object name = dog.getValue().get("name")  //<---- ok 

-- but --

String name = dog.getValue().get("name")  //<---- won't compile

As by this stage, the type information has been lost and you are left with a map that maps String to Object

Upvotes: 0

ChiefGokhlayeh
ChiefGokhlayeh

Reputation: 93

Your Map<String, A> has the undefined type A in its declaration. The problem is, that A is never specified and therefore can not be used as a Type for anyting.

The solution would be to either use the wildcard type or use Object instead of A.

Here are the two example solutions: Map<String, ?> data = new HashMap <String, ?>(); or Map<String, Object> data = new HashMap <String, Object>();

The same thing has to be applied to the map in your Dog class.

Upvotes: 0

Louis Wasserman
Louis Wasserman

Reputation: 198221

You should not be using a Map for this; this is not like PHP.

Instead you should be creating a class that knows the type of each of its fields:

class Animal {
  String name;
  String favfood;
  int someIntegerField;
  List<Foo> someListField;
  ...
}

You should really only be using a Map when all the keys and all the values have the same type.

Upvotes: 1

libik
libik

Reputation: 23049

A cannot be resolved to a type

It just means that you are using class "A" which does not exist. For example here Map<String, A> data you are saying, that you are creating Map, where string is key and the value is type "A".

In comparision, this Map<String, Integer> would mean, that you create map, which has string as key and integer as associated value to its key.

Upvotes: 0

Related Questions