我有一个代码(我正在测试Map接口)。
这是我的主要方法:
import blue.*;
public class Test {
public static void main(String[] args) throws InterruptedException, java.io.IOException {
MapCollections.<String, Number>frequencyTable(args);
}
}
这是我使用的MapCollections
课程:
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);
};
}
错误是:
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
我知道我无法将K
和V
从一种方法转移到另一种方法,但它们的含义却失去了意义。
1)但为什么呢?
2)什么是K#1, V#1, K#2, V#2
?为什么会出现其他数量?
3)有没有办法让K
和V
按照我的意愿使用?
答案 0 :(得分:2)
使用x extends AnyType
时无法添加到集合中。
<强>原因:强> 考虑这个例子:
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
现在你有List<? extends Animal>
public void someMethod(List<? extends Animal> list) {
list.add(new dog()); //no valid
}
然后你调用这样的方法:
List<Cat> catList = new ArrayList<Cat>();
somemethod(catList);
如果在使用带有扩展名的通配符时允许添加到集合中,则只需将Dog添加到仅接受Cat或子类型类型的集合中。因此,您无法在使用带有上边界的通配符的集合中添加任何内容。
BTW,字符串是最终类而k extends String
虽然编译,但无效,因为没有什么可以扩展最终类的
答案 1 :(得分:1)
问题是K extends String
可能是也可能不是String的子类(泛型忽略了类是final
的事实)这意味着你可以更新键和值可能是这些班级的另一个子类。
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.
我会删除K extends String
,因为它没有添加任何值,因为唯一有效的选项是String
。如果您只打算查找,则可以V extends Number
,而不是修改集合。
在这个具体的例子中,我只是将Number a Integer和它留在那里,除非你期望计数超过20亿。如果你担心空间我会使用TObjectIntHashMap,这使得更容易更新这样的集合。 e.g。
TObjectIntHashMap<String> counters = new TObjectIntHashMap<>();
counters.adjustOrPut("Hello", 1, 1); // one liner to add or increment for a key.
答案 2 :(得分:1)
按照给出的顺序回答您的问题(答案编号与您的问题相对应)
1)GanGnaMStYleOverFlowErroR已经回答了这个问题。让我试着用你自己的例子。
您已使用Test类中的类型参数调用frequencyTable方法
MapCollections.<String, Number>frequencyTable(args);
所以你希望你的hm.put(“one”,1)成功。 String是不可扩展的,但为了进一步解释,我们假设它是并假设有一个名为SplittableString的子类。
让我们说其他人正在使用您的代码并想要致电
MapCollections.<SplittableString, Number>frequencyTable(args);
他希望他的键成为子类型(SplittableString)。但是您正在使用父类'对象作为键加载(在您的方法中)。调用您的方法后,另一个开发人员尝试检索该密钥,假设他将获得SplittableString。但实际上他得到的是String,它不能分配给它的子类'reference。
2)你在这两种方法中所推出的K和V是不同的。
public static <K extends String, V extends Number> void frequencyTable(K[] args) {...}
public static <K, V> Map<K, V> getHashMap(int initialCapacity) {...}
为了在错误消息中区分它们,编译器使用K#1,K#2和V#1,V#2。它实际上试图将它们(在错误消息中)表示为
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)不要将值放在你的hm.put(“one”,1)中,而是使用类似的东西
public static <K extends String, V extends Number> void frequencyTable(K[] args, V[] values) {
pm.put(args[0], values[0]);
}
通过这种方式,你可以放置调用者想要输入的类型的对象,而不是放入不仅用户不想要的东西,而且还可能破坏类型的安全性。