hyde
hyde

Reputation: 62848

What is proper way to define Map<String, ?> method parameter

I have a custom Map to String method. It is meant to be used with any kind of Map that has String keys, not restricted to some specific map value type. The map values might have come from javax.jms.Message.getObjectProperty(String name) method, for example, or just be plain Strings.

Which of following is the most "proper" method signature to use, and why, or are all equal?

String map2String(Map<String, Object> map){...}

or

String map2String(Map<String, ?> map){...}

or

String map2String(Map<String, ? extends Object> map){...}

or (added edit)

<E> String map2String(Map<String, ? extends E> map){...}

or something else?

Also, the method contains for-each loop somewhat like this:

for(Entry<String, ?> entry : map.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue().toString();
}

Which is "proper" type for the entry variable, does it matter (except the incompatible combination of ? in the map, Object in the entry type).

Upvotes: 3

Views: 2261

Answers (3)

skuntsel
skuntsel

Reputation: 11742

Dealing with collections I would choose inheritance and generics as principles.

For instance, I would declare an interface, or an abstract base class and will pass it as the collection type parameter, like:

public abstract class AnimalBaseClass {
    //...
    public abstract void makeSound();
}

public interface AnimalInterface {
    public void makeSound();
}

Then the collection will turn into either Map<String, AnimalInterface> or Map<String, ? extends AnimalBaseClass>. This way, first map will accept any class that implements AnimalInterface, the second map will accept any subclass of abstract base class AnimalBaseClass, like Dog extends AnimalBaseClass.

As for your question concerning wildtypes in defining lists the answer lies in inheritance principles of java generics, namely: List<Integer> is not a subtype of List<Number>, but List<Integer> or List<? extends Integer> is a subtype of List<? extends Number>. So, if you have List<Integer> li and List<Number> ln you can't equate ln = li. But if you declare ln as List<? extends Number> ln the equation will be valid. But don't forget that though the equation is valid, adding Numbers to the list will cause a compile-time error, like in ln.add(new Float()), so your reference will be more like a 'read-only' version of contents.

And so, your generic method will turn into:

public <T extends YourBaseClass> String map2String(Map<String, T> map) {
    //do something with your map and return a String object
}

And, if you want Object to be your base class, just omit 'extends YourBaseClass'.

Upvotes: 0

Maxim Shoustin
Maxim Shoustin

Reputation: 77910

The best way is to use 1st example you posted just instead Object use your class type. Otherwise you must cast Object to other class or use instanceof and it's not good idea

 String map2String(Map<String, YourClass> map){...}

About other ones:

String map2String(Map<String, ? extends Object> map){...}

Well, ? extends Object is not the best bet since every Class derived (subtype) from Object. There is no distinction between ? extends Object and ?.

extends wildcard you can use in case if:

class MyClass extends RootClass
....
class YourClass extends RootClass
....
String map2String(Map<String, ? extends RootClass> map)   // this method can fetch argumets by type of Map  `MyClass` and `YourClass`.

further:

String map2String(Map<String, ?> map){...}

Lets say you used String map2String(Map map){...} without any type. Compiler will tell you: Map is a raw type. References to generic type Map should be parameterized. But your too lazy to think about witch parameter to take. So many times I saw, people type like: String map2String(Map<?,?> map){...} to remove Warnings. But its bad approach.

Upvotes: 1

artbristol
artbristol

Reputation: 32427

String map2String(Map<String, ?> map){...} is correct (? extends Object is redundant)

The first one won't work because you wouldn't be able to call

Map<String,Integer> myMap = {...}
map2String(myMap); // Map<String,Integer>  is not  Map<String,Object>  

The proper type for the entry variable is Entry<String, ?> as you suspected.

Upvotes: 5

Related Questions