我在第二行遇到了错误,所有的项目不都是Object
吗?
Map<String, ?> value; // I get value from some place
Map<String, Object> val = value;
我在第二行遇到了错误,所有的项目不都是Object
吗?
Map<String, ?> value; // I get value from some place
Map<String, Object> val = value;
?
表示它可以被替换为任何类型(这个类型可以是有选择地被限制的)。在泛型的情况下,Object
不能安全地用这种方法替换为任何类型(因为泛型在运行时被擦除,会遇到泛型和子类型化的问题)。因此,这两者在语义上不是完全相同的,这就是出现编译错误的原因。Map<String, ?> value = new HashMap<String, String>();
Map<String, Object> val = value; //This line would fail in real life, but assuming it passes...
val.put("Hello", 0); //We could do this!
这显然毫无意义 - HashMap
的类型是 <String, String>
,但你却放了一个 Integer
进去!
这就是 ?
表示的不同之处 - 除非你放入一个 Object
,否则在地图中放置任何其他东西都会导致编译错误:
value.put("Hello", 0); //Error
值得注意的是,数组不受这些限制的约束,因为它们已被具体化,因此当类型不匹配时可以在运行时抛出有意义的异常。(是否更可取是一个有争议的问题,但个人强烈建议在编译时尽早捕获错误!) 由于泛型不能做到这一点,编译器必须确保其在编译时的安全性。
Map<String, Object> value; // I get an Object
Map<String, ? extends Object> val = value; // Something that is an Object.
或者甚至更好
Map<String, Object> value;
Map<String, ?> val = value; // Will take anything...
// Object is more specific.
不是的。Map<String, Object>
不是 Map<String, ?>
的超类型,所以这个赋值不是类型安全的。
(然而,Map<String, ?>
是所有 Map<String, T>
的超类型。所以你代码的相反情况是可以工作的。)
Map<String, ?>
和Map<String, Object>
都可以潜在地将任何Object
作为值。就你所知道的代码而言,这是你可以假设的最具体的内容。(与.get(key)
相关)
但是占位符?
意味着它可以是不同的具体类型。
例如,Map<String, ?>
在运行时允许是一个Map<String, Apple>
。即使只有理论上可能有苹果映射的情况下,您也不能将具有不同类型的泛型分配给彼此。
?
,您告诉编译器您不知道也不关心泛型类型是什么。如果您只从容器中读取并使用在上限声明的方法(例如,您可以从 Collection<?>
读取并调用 toString()
,或者从 List<? extends Closeable>
读取并调用 close()
),那么这很好,但是将对象放入集合中是不安全的(它可能真的是一个 Collection<URL>
,尝试添加一个 String
将使其损坏)。我是个白痴,把这个留在这里以示羞耻。
C#中泛型的协变和逆变 是一个常见的困惑来源。它在 .NET 4 中被部分修复(并不是说它本身有问题),但让泛型类型按照你所描述的方式工作的新方法是选择性的,而不是追溯性的。
Map<String, ?> value = null; // initialize
Map<String, Object> val = (Map<String, Object>) value; //cast