1

我有一个代码(我正在测试地图接口)。

这是我的主要方法:

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

我明白我不能传递KV从一种方法到另一种方法,它们失去了意义。

1)但是为什么?

2) 是什么K#1, V#1, K#2, V#2?为什么会出现额外的数量?

3)有没有一种方法可以按照我的意愿使用KV

4

3 回答 3

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 或子类型类型的集合中。因此,您不能将任何内容添加到使用带有上限的通配符的集合中。

顺便说一句,String是一个最终类k extends String虽然可以编译,但无效,因为没有什么可以扩展最终类的

于 2012-12-30T14:03:34.900 回答
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 设为 Integer 并保留它,除非您预计计数会超过 20 亿。如果您担心空间问题,我会使用TObjectIntHashMap来更轻松地更新此类集合。例如

TObjectIntHashMap<String> counters = new TObjectIntHashMap<>();
counters.adjustOrPut("Hello", 1, 1); // one liner to add or increment for a key.
于 2012-12-30T14:05:24.540 回答
1

按照给定的顺序回答您的问题(答案编号与您的问题相对应)

1) GanGnaMStYleOverFlowErroR 已经回答了这个问题。还是让我试试你自己的例子。

您已经使用 Test 类中的类型参数调用了 frequencyTable 方法

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

所以你希望你的 hm.put("one", 1) 成功。String 是不可扩展的,但为了进一步解释,让我们假设它是可扩展的,并假设有一个名为 SplittableString 的子类。

假设其他人正在使用您的代码并想致电

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

他希望他的键是子类型(SplittableString)。但是您正在(在您的方法中)使用父类的对象作为键来加载。另一个开发人员在调用您的方法后尝试检索密钥,假设他将获得一个 SplittableString。但实际上他得到的是不能分配给其子类引用的字符串。

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]);
}

这样,您将放置调用者打算放入的类型的对象,而不是放入不仅用户不想要而且可能破坏类型安全的东西。

于 2012-12-30T15:46:28.447 回答