Reputation: 62848
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
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
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
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