简体   繁体   中英

Java Generics: why I cannot pass the Bounded Type Parameters actual in one method to the other method?

I have a code (I'm testing the Map Interface).

Here's my main method:

import blue.*;

public class Test {
    public static void main(String[] args) throws InterruptedException, java.io.IOException {

        MapCollections.<String, Number>frequencyTable(args);
    }
}

And here's my MapCollections class I use:

package blue;
import java.util.*;
import static java.lang.System.out;

public class MapCollections {

   /**
   * The Generic Method. K is String and V is Number.
   */ 
   public static <K extends String, V extends Number> void frequencyTable(K[] args) {
        int initialCapacity = 10;

        // Now I pass the same types (K,V) to the getHashMap method
        // but it doesn't work, I get compile error on the hm.put() call, see a little below...
        // If I explicitly change K,V for String and Number it compiles fine, without errors
        Map<K, V> hm = MapCollections.<K,V>getHashMap(initialCapacity);

        // ... the error is
        // error: method put in interface Map<K#2,V#2> cannot be applied to given types
        hm.put("one", 1);
        hm.put("two", 2);
    };


    public static <K, V> Map<K, V> getHashMap(int initialCapacity) {

        return new HashMap<K,V>(initialCapacity);
    };
}

The error is:

C:\java>javac Test.java
.\blue\MapCollections.java:13: error: method put in interface Map<K#2,V#2> cannot be applied to given
 types;
                hm.put("one", 1);
                  ^
  required: K#1,V#1
  found: String,int
  reason: actual argument String cannot be converted to K#1 by method invocation conversion
  where K#1,V#1,K#2,V#2 are type-variables:
    K#1 extends String declared in method <K#1,V#1>frequencyTable(K#1[])
    V#1 extends Number declared in method <K#1,V#1>frequencyTable(K#1[])
    K#2 extends Object declared in interface Map
    V#2 extends Object declared in interface Map

I've understood that I cannot pass K and V from one method to another, they loose their meaning.

1) But why?

2) What is K#1, V#1, K#2, V#2 ? Why additional number of them appears?

3) And is there a way for K and V to be used as I wanted to?

you cannot add into a collection when using x extends AnyType .

Reason: consider this example:

class Animal {

}
class Dog extends Animal {

}
class Cat extends Animal {

}

Now you have List<? extends Animal> List<? extends Animal>

public void someMethod(List<? extends Animal> list) {
    list.add(new dog()); //no valid
}

and you invoke the method like this:

List<Cat> catList = new ArrayList<Cat>(); 
somemethod(catList);

If it were allowed to add in the collection when using wildcards with extends you just added a Dog into a collection which accepts only Cat or subtype type. Thus you cant add anything into the collection which uses wildcards with upperbounds.

BTW, String is a final class and k extends String though compiles, isn't valid as nothing can extends final class's

The problem is that K extends String which may or may not be a sub-class of a String (Generics ignores the fact the class is final ) This means you could be updating the key and value which could be another sub-class of these class.

Map<String, Double> map = new HashMap<>();
Map<String, ? extends Number> map2 = map; // ok
Number n = map2.get("Hello"); // ok
map2.put("Hello", 1); // not ok.

I would remove K extends String as it adds no value given the only valid option is String . You can make V extends Number provided you only intend to lookup, not modify the collection.

In this specific example, I would just make Number an Integer and leave it at that unless you expect the count to exceed 2 billion. If you are concerned about space I would use TObjectIntHashMap which make updating such a collection easier. eg

TObjectIntHashMap<String> counters = new TObjectIntHashMap<>();
counters.adjustOrPut("Hello", 1, 1); // one liner to add or increment for a key.

To answer your questions in the order given (the answer numbers correspond to your questions)

1) GanGnaMStYleOverFlowErroR has already answered this. Still let me try with your own example.

You have called the frequencyTable method with the type paramters in the Test class as

MapCollections.<String, Number>frequencyTable(args);

So you expect your hm.put("one", 1) to succeed. String is not extensible, but for further explanation let us assume that it is and assume that there is a subclass called SplittableString.

Let us say someone else is using your code and wants to call

MapCollections.<SplittableString, Number>frequencyTable(args);

He wants his keys to be the subtype (SplittableString). But you are loading (in your method) with parent class' objects as keys. The other developer after calling your method tries to retrieve the key assuming that he will be getting a SplittableString. But in fact what he gets back is String which cannot be assigned to its subclass' reference.

2) The K and V that you delared in both the methods are different.

public static <K extends String, V extends Number> void frequencyTable(K[] args) {...}

public static <K, V> Map<K, V> getHashMap(int initialCapacity) {...}

To differentiate both of them in the error message the compiler uses K#1, K#2 and V#1, V#2. It actually tries to represent them (in the error message) as

public static <K#1 extends String, V#1 extends Number> void frequencyTable(K[] args) {...}

public static <K#2, V#2> Map<K, V> getHashMap(int initialCapacity) {...}

3) Instead of putting the values in your hm.put("one", 1) use something like this

public static <K extends String, V extends Number> void frequencyTable(K[] args, V[] values) {
    pm.put(args[0], values[0]);
}

With this you are putting the objects of types that the caller meant to put in, instead of putting in something that not only the user doesn't want but also may break the type safety.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM