繁体   English   中英

Java Generics:为什么我不能将一个方法中的Bounded Type Parameters实际传递给另一个方法?

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

我有一个代码(我正在测试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

我知道我不能将KV从一种方法传递到另一种方法,它们会失去意义。

1)但为什么呢?

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

3)有没有办法让KV按我的意愿使用?

使用x extends AnyType时无法添加到集合中。

原因:考虑这个例子:

class Animal {

}
class Dog extends Animal {

}
class Cat extends Animal {

}

现在你有List<? 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, String是一个最终类k extends String虽然编译,但无效,因为没有什么可以扩展最终类

问题是K extends String ,它可能是也可能不是K extends String的子类(Generics忽略了类是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 ,这使得更容易更新这样的集合。 例如

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

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

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

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

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

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

让我们说别人正在使用您的代码并想打电话

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

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

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

通过这种方式,您可以放置​​调用者想要放入的类型的对象,而不是放入不仅用户不想要的东西,而且还可能破坏类型安全性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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