几年前,当Java7已经发布一年左右时,我已经从C#回到Java了 - 在某些方面,java泛型对我来说似乎很奇怪。
有一个我一遍又一遍地回来,看到这两个答案的受欢迎程度......
...似乎很多人(包括我)“自然地”会在“泛型集合”的声明中期望语法的不同行为。考虑下面的代码示例......
注意:架构正确性超出范围 - 假设baseClassesStorage
应该可用于外部世界的get / add
public class DummyClass {
public static class BaseClass {}
public static interface ListOfAtLeastBaseClasses<T extends BaseClass> extends List<T> {}
public ListOfAtLeastBaseClasses<?> baseClassesStorage;
public void addToStorage(BaseClass item) {
baseClassesStorage.add(item);
}
}
...来自非Java世界的典型程序员会认为ListOfAtLeastBaseClasses<?> baseClassesStorage
说“这是从BaseClass
派生的项目的存储”,但是这个样本的汇编在baseClassesStorage.add(item)
失败并出现以下错误:
C:\Temp\...\DummyClass.java:14: error: no suitable method found for add(BaseClass)
baseClassesStorage.add(item);
^
method Collection.add(CAP#1) is not applicable
(argument mismatch; BaseClass cannot be converted to CAP#1)
method List.add(CAP#1) is not applicable
(argument mismatch; BaseClass cannot be converted to CAP#1)
where CAP#1 is a fresh type-variable:
CAP#1 extends BaseClass from capture of ?
这种行为的原因在两个above mentioned answers
中描述。
但是,如何以“正确”和“优雅”的方式实施我的用例?
我知道的选项:
似乎'正确的Java方式'将变量声明为
public ListOfAtLeastBaseClasses<BaseClass> baseClassesStorage;
但是这样我实际上有'List'下限的冗余定义 - 它已经在ListOfAtLeastBaseClasses类的'contract'中定义了。这里的另一个问题 - 如果在某些时候我想要更改ListOfAtLeastBaseClasses类的下限限制,那么我将需要遍历此类型的所有成员变量并更改其定义:(。
使用'raw'类型
public ListOfAtLeastBaseClasses baseClassesStorage;
这将编译,但有效地删除类型检查并在构建期间生成不需要的警告。
......还有什么可能吗? ...
ListOfAtLeastBaseClasses<?> baseClassesStorage;
- 在Java中是另一回事ListOfAtLeastBaseClasses<> baseClassesStorage;
- 编译错误ListOfAtLeastBaseClasses<*> baseClassesStorage;
- 编译错误答案 0 :(得分:2)
似乎正确的Java方式&#39;将变量声明为
public ListOfAtLeastBaseClasses<BaseClass> baseClassesStorage;
但是这样我实际上对该下界的冗余定义&#39; List&#39;
不是真的。当您定义泛型类型时,您说这个类的用户将能够声明像
这样的变量ListOfAtLeastBaseClasses<Foo> listOfFoo;
ListOfAtLeastBaseClasses<Bar> listOfBar;
其中Foo和Bar必须是BaseClass的子类。 BaseClass是一个上限。但泛型类型的用户可以选择列表更具限制性,因此只接受Foo或Bar的实例。
当您将列表声明为
时ListOfAtLeastBaseClasses<BaseClass>
您选择创建一个可以接受任何类型的BaseClass实例的列表。
我们可以想象一个特殊的语法来说ListofAtLeastBaseClasses<TheDefinedUpperBound>
,但这不是钻石运算符的用途,而且特殊的语法会给IMO带来不必要的复杂性。