如何使用“待定”为java通用映射添加值&引用;值类型

如何使用“待定”为java通用映射添加值&引用;值类型,java,dictionary,generics,types,Java,Dictionary,Generics,Types,我在jdk 8示例中见过这种声明: Map<String, ?> map = new HashMap<>(3);//OK 两者都无法编译。我想知道: (1) 在上面的地图声明中“?”表示什么 (2) 如何给这个“映射”赋值?map是一种抽象类型。如果变量具有此类型,则它可以引用具有以下任何类型(以及其他类型)的对象 HashMap TreeMap HashMap TreeMap 显然,可能性基本上是无限的。如果您有这种类型的变量,您知道它引用的映射的键是St

我在jdk 8示例中见过这种声明:

    Map<String, ?> map = new HashMap<>(3);//OK
两者都无法编译。我想知道:

(1) 在上面的地图声明中“?”表示什么

(2) 如何给这个“映射”赋值?

map
是一种抽象类型。如果变量具有此类型,则它可以引用具有以下任何类型(以及其他类型)的对象

  • HashMap
  • TreeMap
  • HashMap
  • TreeMap
显然,可能性基本上是无限的。如果您有这种类型的变量,您知道它引用的映射的键是
String
,但实际上您不知道其他任何东西。特别是,当您在该
贴图上执行
get
时,您不知道最终将得到什么类型的对象

但更重要的是,如果没有某种讨厌的、不安全的施法操作,你将永远无法
将任何东西放入地图中。编译器会阻止你。在你给出的例子中-

  • map.put(“abc”,可选第(5)部分)
    不会编译,因为
    map
    可能是一个
    HashMap
    ,您不能在其中放入
    可选的
  • map.put(“kk”、“xyz”)
    不会编译,因为
    映射
    可能是一个
    树映射
    ,您不能将
    字符串
    放入其中
例外情况可能是
null
,或者来自地图本身的任何值-有关这些可能性的更多详细信息,请参阅Andy Turner的优秀答案

简而言之,如果您有一个
Map
类型的变量,编译器将允许您对其执行的操作有点有限。您不能将任何内容放入映射,除非它是
null
或它已经在映射中。您所能做的就是从映射中获取值,然后从映射中删除值

因此,使用
Map
变量是非常有限的。如果您只想从映射中读取值,那么这当然很好。但是不要期望能够在映射中插入任意值,除非使用不同的表达式来引用映射。

问题(1)

? is called wildcard generic type.It can be used with any type,
but you need to specify type fist as Map is an abstract class.
问题(2)

您可以使用上界或下界为该地图赋值。

以下是使用上限或下限时的指导原则

? extends Type   - is used for read access only
? super Type     - is used for write access only.
PECS简而言之产生(写访问)-使用扩展,而消耗(读访问)使用超级

Map<String, ? super Object> map = new HashMap<>(3);//OK
    map.put("abc",Optional.of(5));
    map.put("kk","xyz");
map.forEach((k,v)->System.out.println(k+"\t"+v));
附加注释

无界通配符

 List<?> l = new ArrayList<String>();
listl=newarraylist();
带上限的通配符?扩展类型

 List<? extends Exception> l = new ArrayList<RuntimeException>();

列表
是一种未知类型:您不知道代码中该点是什么

因此,除了几个例外,您无法安全地向地图添加任何值

您可以添加literal
null
(因为
null
可以强制转换为任何类型):

此外,您还可以向地图添加从地图中获取的值:

<T> void reAdd(Map<String, T> map) {
  T value = map.get("123");
  map.put("456", value);
}

// Call with
reAdd(map); // compiles
void reAdd(地图){
T值=map.get(“123”);
地图放置(“456”,数值);
}
//打电话给
读(地图);//汇编

是未知类型,是java。无法添加未知内容。请不要将通配符有界映射称为“只读”。一方面,您可以删除元素;另一方面,你可以添加元素(见我的答案)。@Andy你说得很对。我只是在读(并投票)你的答案。我从未想过要添加一个来自地图本身的值。我会重述这一部分。我想知道这个结构有什么用。我可以想象一个抽象类用这个作为返回类型定义一个方法,但是我能想到的任何实现都会违反liskov替换原则或开闭原则。()嗯,@tgr,您可以编写将映射视为只读的方法。例如,您可以编写一个方法来打印映射中的所有值。同意,打印是一种机制,不需要对值类型进行任何假设。除此之外呢?我看不出只读映射有多大价值,需要对其值进行转换以供进一步使用。
 List<? extends Exception> l = new ArrayList<RuntimeException>();
 List<? super Exception> l = new ArrayList<Object>();
map.put("abc", null); // compiles
<T> void reAdd(Map<String, T> map) {
  T value = map.get("123");
  map.put("456", value);
}

// Call with
reAdd(map); // compiles