<有什么区别?扩展 Base> <T 扩展 Base>?

2022-09-01 13:21:17

在此示例中:

import java.util.*;

public class Example {
    static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
    static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

    static void function(List<? extends Number> outer)
    {
        doesntCompile(new HashMap<Integer, List<Integer>>());
        compiles(new HashMap<Integer, List<Integer>>());
    }
}

doesntCompile()无法使用以下命令进行编译:

Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
        doesntCompile(new HashMap<Integer, List<Integer>>());
                      ^

而编译器接受。compiles()

这个答案解释了唯一的区别是 不像 ,允许您稍后引用该类型,但事实似乎并非如此。<? ...><T ...>

在这种情况下,和 之间有什么区别,为什么第一个不能编译?<? extends Number><T extends Number>


答案 1

通过使用以下签名定义方法:

static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

并像这样调用它:

compiles(new HashMap<Integer, List<Integer>>());

您正在根据您提供的类型进行匹配。T

在jls §8.1.2中,我们发现(有趣的部分由我加粗):

泛型类声明定义一组参数化类型 (§4.5),每个类型参数对应一个按类型参数调用类型参数部分的可能类型。所有这些参数化类型在运行时共享同一类。

换句话说,该类型与输入类型匹配并赋值 。签名将有效地变为 。TIntegerstatic void compiles(Map<Integer, List<Integer>> map)

在方法方面,jls定义了子类型规则(§4.5.1,由我加粗):doesntCompile

如果 T2 表示的类型集可证明是 T1 在以下规则的自反和传递闭包下表示的类型集的子集(其中<:表示子类型 (§4.10)),则类型参数 T1 称为包含另一个类型参数 T2,编写为 T2 <= T1:

  • ?扩展 T <= ?如果 T <,则扩展 S:S

  • ?扩展 T <= ?

  • ?超级 T <= ?如果 S <:T,则为超级 S

  • ?超级 T <= ?

  • ?超级 T <= ?扩展对象

  • T <= T

  • T <= ?扩展 T

  • T <= ?超级T

这意味着,这确实包含甚至包含 ,但 和 的情况并非如此。有关该主题的更多信息,请参阅此SO线程。您仍然可以通过声明来使带有通配符的版本工作,即您希望子类型为:? extends NumberIntegerList<? extends Number>List<Integer>Map<Integer, List<? extends Number>>Map<Integer, List<Integer>>?List<? extends Number>

public class Example {
    // now it compiles
    static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
    static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

    public static void main(String[] args) {
        doesntCompile(new HashMap<Integer, List<Integer>>());
        compiles(new HashMap<Integer, List<Integer>>());
    }
}

答案 2

在通话中:

compiles(new HashMap<Integer, List<Integer>>());

T 与 Integer 匹配,因此参数的类型为 。方法并非如此:无论调用中的实际参数如何,参数的类型都保持不变;并且无法从 中分配。Map<Integer,List<Integer>>doesntCompileMap<Integer, List<? extends Number>>HashMap<Integer, List<Integer>>

更新

在方法中,没有什么可以阻止您执行如下操作:doesntCompile

static void doesntCompile(Map<Integer, List<? extends Number>> map) {
    map.put(1, new ArrayList<Double>());
}

所以很明显,它不能接受a作为论点。HashMap<Integer, List<Integer>>