如何将具有整数值的条目添加到 Map<String, ?>
How to add an entry with an Integer value into Map<String, ?>
我必须将一个整数值放入下面的地图中。
Map<String,?> map
map.put("key",2000);
当我 运行 上述代码时,出现以下错误:
incompatible types: java.lang.Integer cannot be converted to capture#1 of ?
Map<String, Integer> map = new Map<String, Integer>;
map.put("key",2000);
您可以将很多东西赋值给 Map<String, ?>
类型的变量。例如:
Map<String, ?> map;
map = new HashMap<String, Object>(); // works
map = new HashMap<String, String>(); // works
map = new HashMap<String, Number>(); // works
map = new HashMap<String, Integer>(); // works
一切正常。因此,假设已将 map = new HashMap<String, String>()
分配给它。然后你尝试 运行 .put("key", 2000);
就可以了。
这显然没有意义。因此,为什么该代码无法编译。
我假设你的眼球和人脑看过这段代码并确定你那里的 map
除了 Map<String, Object>
或Map<String, Number>
或 Map<String, Integer>
甚至 Map<String, Serializable>
- .put("key", 2000)
适合的类型。
但这不是 java 的工作方式。类型的一部分要点是限制你自己,给你自己以后分配其他东西的自由。
完全类似于此:
Object x = "hello";
x.toLowerCase();
上面的不编译: Object
类型没有toLowerCase()
方法。是的,是的,我们的眼球和大脑看着这个然后走:那是愚蠢的,当然是这样!看,x
很明显指向一个字符串!
但在这种情况下,只需.. 写 String x = "hello"
.
因此,这同样适用于您的场景。只需写 Map<String, Integer>
即可。
很遗憾....
有时您有动态代码想要检查事物是什么并采取相应的行动。例如:
void foo(Object o) {
if (o instanceof String s) return s.toLowerCase();
throw new SomeException();
}
但是您不能对泛型执行此操作 - 无法 instanceof
检查泛型。你不能做这样的事情: IF 我的 map
变量指向的对象有 Integer
作为值类型(或 Integer 的任何超类型),然后做map.put("key", 2000)
,否则不要。
这不是泛型在 java 中的工作方式。根本没有办法做到这一点。
你唯一能做的就是下达命令。编写代码,如果它是真的就可以正常工作,但如果它不是真的,一切都会变得混乱:你可以在地图上强制 运行 .put("key", 2000)
,这将把它放在地图上,如果那是map 声明为 Map<String, String>
,那么与此地图交互的任何代码都将开始左右抛出 ClassCastExceptions
,即使它们从未投射任何东西。因为您将整数推入了字符串映射。
因此,为什么编译器会在您强制执行此操作时大喊大叫,以及为什么您不应该这样做。泛型的唯一目的是让你的编程生活更轻松,阐明 API 并捕捉错误,而你……通过强制它来做相反的事情。但是,嘿,如果你真的想这样做(极不可能):
void example(Map<String, ?> map) {
Map forceIt = map; // raw assignment
forceIt.put("key", 2000); // compiles
}
以上编译并执行您要求的 - 但会警告您这是一个非常糟糕的主意。
of (o instanceof String) return ((String) o).toLowerCase();
Unbounded 通用 collections (Collection<?>
) 不可写。让我们来探究一下原因。
未知类型 - <?>
尖括号<?>
中的问号称为未知类型。对于编译器,这意味着 实际类型 无法预测,并且在运行时它可能看起来非常匹配任何东西:Object
、String
,一个Cat
。因此,您从 unbounded collection 中检索到的所有内容都将被视为 Object
:
Collection<?> items = new ArrayList<>();
Object item = items.iterator().next(); // this assignment is safe and will compile
但是不要混淆一个无界collectionCollection<?>
和一个collection objects Collection<Object>
。它们 不兼容.
Collection<Object>
具有 不变行为 ,即它只需要 collection 类型 Object
] 分配给它。同时,我们可以将any类型的collection赋值给Collection<?>
.
Collection<?> items = new ArrayList<String>(); // OK
Collection<Object> objects1 = items; // that will not compile
Collection<Object> objects2 = new ArrayList<String>(); // that will not compile
Collection<Object> objects3 = new ArrayList<Object>(); // OK - compiler will not complain here
因此,未知类型 可以用 扩展 Object
的任何东西表示。您可能会认为 Collection<?>
好像是 upper-bounded collection Collection<? extends Object>
。它们绝对兼容.
Collection<?> items = new ArrayList<>();
Collection<? extends Object> upperBounded = items; // no issues
items = upperBounded; // no issues
修改无界collection
Wild-cards 像 <?>
或 <? extends Object>
是 不是实际类型。它们只是告诉编译器我们不知道 type 在运行时会是什么的一种方式。
那么Collection<?>
的实际类型是什么?
因为已经很清楚 未知类型 (<?>
) 意味着它可以是 任何类型 扩展 Object
,因此在 Collection<?>
的背后可能会出现 ArrayList<Object>
、ArrayList<String>
、HashSet<BigDecimal>
等
Remainder:参数化的collection会在运行时伪装成Collection<?>
是invariant,这意味着 ArrayList<String>
预计仅包含 String
s,而 HashSet<BigDecimal>
预计仅存储 BigDecimal
的实例。但是没有办法在运行时确保这一点,因为在编译期间泛型参数 get erased。因此,在使用无限泛型 collection 的同时提供 type-safety 的唯一方法是不允许向其中添加任何内容。
例如,由于将 Integer
或 Object
添加到 ArrayList<String>
或添加实例 是非法的 String
到 HashSet<BigDecimal>
编译器将 阻止 任何尝试 添加 任何东西到 Collection<?>
。因为没有与 未知类型.
兼容的类型
只有一个例外,null
是任何类型 object 的有效值,因此它也可以添加到 unbounded 中作为 upper-bounded collection.
Collection<?> items = new ArrayList<>();
items.add(null);
System.out.println(items);
items.remove(null);
System.out.println(items.isEmpty());
将给出一个输出:
[null] - null-element was successfuly added
true - i.e. isEmpty
注意:有没有 对 删除 元素的限制 unbounded 和 upper-bounded collection s.
备选方案
因此,没办法向无限映射添加新条目,您可以要么一个一个地复制其条目,执行 instanceOf
检查到常规通用映射 Map<String, Integer>
通过将映射分配给 行类型 [=143= 的变量来完全摆脱泛型],然后在需要检索 value 时手动添加 type-casts,如 Java 1.4.
如何将 unbounded map 的内容复制到 regular 并利用泛型的优点。
Map<String, ?> unboundedMap = new HashMap<>()
Map<String, Integer> writableMap = new HashMap<>();
for (Map.Entry<String, ?> entry: unboundedMap.entrySet()) {
if (entry.getValue() instanceof Integer) {
writableMap.put(entry.getKey(), (Integer) entry.getValue());
}
}
使用 Stream 也一样API
Map<String, Integer> writableMap =
unboundedMap.entrySet().stream()
.filter(entry -> entry.getValue() instanceof Integer)
.collect(Collectors.toMap(Map.Entry::getKey,
entry -> (Integer) entry.getValue()));
我必须将一个整数值放入下面的地图中。
Map<String,?> map
map.put("key",2000);
当我 运行 上述代码时,出现以下错误:
incompatible types: java.lang.Integer cannot be converted to capture#1 of ?
Map<String, Integer> map = new Map<String, Integer>;
map.put("key",2000);
您可以将很多东西赋值给 Map<String, ?>
类型的变量。例如:
Map<String, ?> map;
map = new HashMap<String, Object>(); // works
map = new HashMap<String, String>(); // works
map = new HashMap<String, Number>(); // works
map = new HashMap<String, Integer>(); // works
一切正常。因此,假设已将 map = new HashMap<String, String>()
分配给它。然后你尝试 运行 .put("key", 2000);
就可以了。
这显然没有意义。因此,为什么该代码无法编译。
我假设你的眼球和人脑看过这段代码并确定你那里的 map
除了 Map<String, Object>
或Map<String, Number>
或 Map<String, Integer>
甚至 Map<String, Serializable>
- .put("key", 2000)
适合的类型。
但这不是 java 的工作方式。类型的一部分要点是限制你自己,给你自己以后分配其他东西的自由。
完全类似于此:
Object x = "hello";
x.toLowerCase();
上面的不编译: Object
类型没有toLowerCase()
方法。是的,是的,我们的眼球和大脑看着这个然后走:那是愚蠢的,当然是这样!看,x
很明显指向一个字符串!
但在这种情况下,只需.. 写 String x = "hello"
.
因此,这同样适用于您的场景。只需写 Map<String, Integer>
即可。
很遗憾....
有时您有动态代码想要检查事物是什么并采取相应的行动。例如:
void foo(Object o) {
if (o instanceof String s) return s.toLowerCase();
throw new SomeException();
}
但是您不能对泛型执行此操作 - 无法 instanceof
检查泛型。你不能做这样的事情: IF 我的 map
变量指向的对象有 Integer
作为值类型(或 Integer 的任何超类型),然后做map.put("key", 2000)
,否则不要。
这不是泛型在 java 中的工作方式。根本没有办法做到这一点。
你唯一能做的就是下达命令。编写代码,如果它是真的就可以正常工作,但如果它不是真的,一切都会变得混乱:你可以在地图上强制 运行 .put("key", 2000)
,这将把它放在地图上,如果那是map 声明为 Map<String, String>
,那么与此地图交互的任何代码都将开始左右抛出 ClassCastExceptions
,即使它们从未投射任何东西。因为您将整数推入了字符串映射。
因此,为什么编译器会在您强制执行此操作时大喊大叫,以及为什么您不应该这样做。泛型的唯一目的是让你的编程生活更轻松,阐明 API 并捕捉错误,而你……通过强制它来做相反的事情。但是,嘿,如果你真的想这样做(极不可能):
void example(Map<String, ?> map) {
Map forceIt = map; // raw assignment
forceIt.put("key", 2000); // compiles
}
以上编译并执行您要求的 - 但会警告您这是一个非常糟糕的主意。
of (o instanceof String) return ((String) o).toLowerCase();
Unbounded 通用 collections (Collection<?>
) 不可写。让我们来探究一下原因。
未知类型 - <?>
尖括号<?>
中的问号称为未知类型。对于编译器,这意味着 实际类型 无法预测,并且在运行时它可能看起来非常匹配任何东西:Object
、String
,一个Cat
。因此,您从 unbounded collection 中检索到的所有内容都将被视为 Object
:
Collection<?> items = new ArrayList<>();
Object item = items.iterator().next(); // this assignment is safe and will compile
但是不要混淆一个无界collectionCollection<?>
和一个collection objects Collection<Object>
。它们 不兼容.
Collection<Object>
具有 不变行为 ,即它只需要 collection 类型 Object
] 分配给它。同时,我们可以将any类型的collection赋值给Collection<?>
.
Collection<?> items = new ArrayList<String>(); // OK
Collection<Object> objects1 = items; // that will not compile
Collection<Object> objects2 = new ArrayList<String>(); // that will not compile
Collection<Object> objects3 = new ArrayList<Object>(); // OK - compiler will not complain here
因此,未知类型 可以用 扩展 Object
的任何东西表示。您可能会认为 Collection<?>
好像是 upper-bounded collection Collection<? extends Object>
。它们绝对兼容.
Collection<?> items = new ArrayList<>();
Collection<? extends Object> upperBounded = items; // no issues
items = upperBounded; // no issues
修改无界collection
Wild-cards 像 <?>
或 <? extends Object>
是 不是实际类型。它们只是告诉编译器我们不知道 type 在运行时会是什么的一种方式。
那么Collection<?>
的实际类型是什么?
因为已经很清楚 未知类型 (<?>
) 意味着它可以是 任何类型 扩展 Object
,因此在 Collection<?>
的背后可能会出现 ArrayList<Object>
、ArrayList<String>
、HashSet<BigDecimal>
等
Remainder:参数化的collection会在运行时伪装成Collection<?>
是invariant,这意味着 ArrayList<String>
预计仅包含 String
s,而 HashSet<BigDecimal>
预计仅存储 BigDecimal
的实例。但是没有办法在运行时确保这一点,因为在编译期间泛型参数 get erased。因此,在使用无限泛型 collection 的同时提供 type-safety 的唯一方法是不允许向其中添加任何内容。
例如,由于将 Integer
或 Object
添加到 ArrayList<String>
或添加实例 是非法的 String
到 HashSet<BigDecimal>
编译器将 阻止 任何尝试 添加 任何东西到 Collection<?>
。因为没有与 未知类型.
只有一个例外,null
是任何类型 object 的有效值,因此它也可以添加到 unbounded 中作为 upper-bounded collection.
Collection<?> items = new ArrayList<>();
items.add(null);
System.out.println(items);
items.remove(null);
System.out.println(items.isEmpty());
将给出一个输出:
[null] - null-element was successfuly added
true - i.e. isEmpty
注意:有没有 对 删除 元素的限制 unbounded 和 upper-bounded collection s.
备选方案
因此,没办法向无限映射添加新条目,您可以要么一个一个地复制其条目,执行 instanceOf
检查到常规通用映射 Map<String, Integer>
通过将映射分配给 行类型 [=143= 的变量来完全摆脱泛型],然后在需要检索 value 时手动添加 type-casts,如 Java 1.4.
如何将 unbounded map 的内容复制到 regular 并利用泛型的优点。
Map<String, ?> unboundedMap = new HashMap<>()
Map<String, Integer> writableMap = new HashMap<>();
for (Map.Entry<String, ?> entry: unboundedMap.entrySet()) {
if (entry.getValue() instanceof Integer) {
writableMap.put(entry.getKey(), (Integer) entry.getValue());
}
}
使用 Stream 也一样API
Map<String, Integer> writableMap =
unboundedMap.entrySet().stream()
.filter(entry -> entry.getValue() instanceof Integer)
.collect(Collectors.toMap(Map.Entry::getKey,
entry -> (Integer) entry.getValue()));