在Java中定义变量时是否缺少“Diamond运算符”?

时间:2016-12-03 15:08:16

标签: java generics

几年前,当Java7已经发布一年左右时,我已经从C#回到Java了 - 在某些方面,java泛型对我来说似乎很奇怪。

有一个我一遍又一遍地回来,看到这两个答案的受欢迎程度......

  1. Difference between <? super T> and <? extends T> in Java
  2. What is PECS (Producer Extends Consumer Super)?
  3. ...似乎很多人(包括我)“自然地”会在“泛型集合”的声明中期望语法的不同行为。考虑下面的代码示例......

    注意:架构正确性超出范围 - 假设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中描述。

    但是,如何以“正确”和“优雅”的方式实施我的用例?

    我知道的选项:

    1. 似乎'正确的Java方式'将变量声明为 public ListOfAtLeastBaseClasses<BaseClass> baseClassesStorage; 但是这样我实际上有'List'下限的冗余定义 - 它已经在ListOfAtLeastBaseClasses类的'contract'中定义了。这里的另一个问题 - 如果在某些时候我想要更改ListOfAtLeastBaseClasses类的下限限制,那么我将需要遍历此类型的所有成员变量并更改其定义:(。

    2. 使用'raw'类型 public ListOfAtLeastBaseClasses baseClassesStorage; 这将编译,但有效地删除类型检查并在构建期间生成不需要的警告。

    3. ......还有什么可能吗? ...

      • ListOfAtLeastBaseClasses<?> baseClassesStorage; - 在Java中是另一回事
      • ListOfAtLeastBaseClasses<> baseClassesStorage; - 编译错误
      • ListOfAtLeastBaseClasses<*> baseClassesStorage; - 编译错误

1 个答案:

答案 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带来不必要的复杂性。