jne
jne

Reputation: 467

Java wildcards and generic methods

I have a class using a Map as field variable:

private Map<String, ?> posts;

And in that same class I have a generic method:

public <T> void log(T message) {
   if (isEnabled) {
        Date time = Calendar.getInstance().getTime();
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss:SS");
        posts.put(sdf.format(time.getTime()), message);
    }
}

However I get a compiler error on the posts.put statement saying:

The method put(String, capture#2-of ?) in the type Map<String,capture#2-of ?> is not 
applicable for the arguments (String, T)

I'm a bit new to wildcards and generic methods so what am I doing wrong?

Upvotes: 0

Views: 170

Answers (2)

Rohit Jain
Rohit Jain

Reputation: 213193

When you declare your Map with an unbounded wildcard type:

private Map<String, ?> posts;

you won't be able to put anything apart from null into it. Because ? is a substitute for any type. Basically, your Map is a supertype of all the instantiation of Map with String as a key, and value of any type. ? can be Date, Number, Cat, Tiger, anything.

Suppose your map is instantiated like this:

private Map<String, ?> posts = new HashMap<String, Date>();

and the type parameter in your method is inferred as String, then your put method is doing:

posts.put(String, String);

...thus trying to add String type where a Date is needed. Certainly that would fail at runtime. Generic type avoids these issue by issuing a compiler error for this.


You can solve this problem by making your class itself generic, thus using the same type parameter in the method and map:

class YourClass<T> {
    private Map<String, T> posts;

    public void log(T message) {
        if (isEnabled) {
            Date time = Calendar.getInstance().getTime();
            SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss:SS");
            posts.put(sdf.format(time.getTime()), message);
        }
    }
}

Also as noted in comments, you should avoid having time as key. Since Date#getTime() has only milliseconds precision, so anything you put during the interval that is covered in a single milliseconds, will be stored as the same key, thus overwriting the old value for that key.

Upvotes: 4

Piotr Kołaczkowski
Piotr Kołaczkowski

Reputation: 2629

Map<String, ?> doesn't mean you can put anything into that map. It means the value class is unknown. But it can be e.g. Foo or Bar. And Foo might be incompatible with any type T, so the compiler complains. Use Map<String, Object> instead or make your T parameter class-level and then parametrize your map with it: Map<String, T>

Upvotes: 4

Related Questions