代码之家  ›  专栏  ›  技术社区  ›  abhijeetpawar

Java列表在初始化过程中是否表现为协变类型?

  •  6
  • abhijeetpawar  · 技术社区  · 7 年前

    我知道Java中的列表是不变的。

    因此,下面的第二条语句给出了预期的编译错误

        List<Integer> integers = Arrays.asList(1, 2, 3);
        List<Number> numbers = integers;
    

    然而,所有这些都很好

        List<Integer> numbers1 = Arrays.asList(1, 2, 3);
        List<? extends Number> numbers2 = Arrays.asList(1, 2, 3);
        List<Number> numbers3 = Arrays.asList(1, 2, 3);
    

    所以我的问题是,上面的最后一条语句是如何编译的?

    我明白这一点 Arrays.asList() 接受调用者的类型,但我认为 Arrays.asList(1,2,3) 将解析为最接近的类型 List<Integer> 并将其设置为 List<Number> 不会编译,因为列表是不变的。

    我错过了什么?

    4 回复  |  直到 7 年前
        1
  •  4
  •   Sergey Kalinichenko    7 年前

    这里没有协方差。相反,Java编译器使用调用的上下文 Arrays.asList<T> 推断 类型 T 您的程序要为方法调用指定的。

    在Java 8之前,可以显式指定类型,例如

    List<Integer> numbers1 = Arrays.<Integer>asList(1, 2, 3);
    List<? extends Number> numbers2 = Arrays.<? extends Number>asList(1, 2, 3);
    List<Number> numbers3 = Arrays.<Number>asList(1, 2, 3);
    

    注意上面的代码片段如何重复该类型 T 在赋值的两侧,Java编译器都会设置推理规则来传播 T 从作业的左侧到右侧,消除重复。

    参考号: Type Inference Tutorial

        2
  •  1
  •   davidxxx    7 年前

    JLS很好地说明了这种情况。
    这在初始化期间不是协方差,因为它也适用于“经典”方法调用。
    请注意,示例中使用的推理策略使其在Java 8中工作,但在Java 7中会失败。

    18.5.2. Invocation Type Inference

    考虑上一节中的示例:

    List<Number> ln = Arrays.asList(1, 2.0);

    最具体的适用方法被确定为:

    public static <T> List<T> asList(T... a)

    为了完成方法调用的类型检查,我们必须确定 与目标类型兼容, List<Number>

    用于证明前一节中适用性的约束集 第B2节为:

    { α <: Object, Integer <: α, Double <: α }

    新的约束公式集如下所示:

    { ‹List<α> → List<Number>› }

    此兼容性约束为±生成一个相等界限,该界限 包含在新的绑定集中,B3:

    { α <: Object, Integer <: α, Double <: α, α = Number }

    这些界限可以轻松解决:

    α = Number

    最后,我们对声明的返回类型执行替换 asList,确定方法调用具有类型List; 显然,这与目标类型兼容。

    此推理策略不同于 Java语言规范,该规范将基于 它的下限(甚至在考虑调用的目标之前 类型),就像我们在上一节中所做的那样。这将导致 错误,因为结果类型不是列表的子类型。

        3
  •  0
  •   zlakad    7 年前

    B/c数字是这些子类的超类:

    直接已知子类: AtomicInteger、AtomicLong、BigDecimal、BigInteger、Byte、Double、DoubleAccumulator、DoubleAdder、Float、Integer、Long、LongAccumulator、LongAdder、Short

    资料来源: https://docs.oracle.com/javase/9/docs/api/java/lang/Number.html

    在您的第一个代码片段中,您正在将“水果”添加到“苹果”篮子中(您无法做到!),在你的第二个例子中,这不是真的。

        4
  •  0
  •   Arthur Noseda    7 年前

    授予, Arrays.asList(1, 2, 3) 产生a List<Integer> 。 然后 列表(<);整数(>); 是有效的 List<? extends Number> 。你可以向自己证明这一点:

    List<? extends Number> l1 = new ArrayList<>();
    List<Integer> l2 = new ArrayList<>();
    l1 = l2;
    

    然而,事实并非如此:

    l2 = l1; // does not compile
    

    Integer 是的子类 Number 所以 列表(<);整数(>); 是有效的 List 延伸的事物 数字