< 和有什么区别?extends Base> 和 <T extends Base>?

Dev*_*ull 29 java generics dictionary jvm arraylist

在这个例子中:

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>>());
    }
}
Run Code Online (Sandbox Code Playgroud)

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>>());
                      ^
Run Code Online (Sandbox Code Playgroud)

whilecompiles()被编译器接受。

这个答案解释了唯一的区别是不同<? ...><T ...>让您稍后引用类型,这似乎并非如此。

是什么区别<? extends Number>,并<T extends Number>在这种情况下,为什么不第一编译?

And*_*cus 15

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

static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
Run Code Online (Sandbox Code Playgroud)

并像这样调用它:

compiles(new HashMap<Integer, List<Integer>>());
Run Code Online (Sandbox Code Playgroud)

您正在匹配T您提供的类型。

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

泛型类声明定义了一组参数化类型(第 4.5 节),一个用于通过类型参数对类型参数部分的每次可能调用。所有这些参数化类型在运行时共享同一个类。

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

说到doesntCompile方法,jls 定义了子类型的规则(第4.5.1 节,我加粗):

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

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

  • ? 扩展 T <= ?

  • ? 超级 T <= ? 超级 S 如果 S <: T

  • ? 超级 T <= ?

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

  • T <= T

  • T <= ? 扩展 T

  • T <= ? 超级T

这意味着,? extends Number确实包含Integer甚至List<? extends Number>包含List<Integer>,但情况并非如此Map<Integer, List<? extends Number>>Map<Integer, List<Integer>>。可以在此 SO 线程中找到有关该主题的更多信息。您仍然可以?通过声明您期望的子类型来使带有通配符的版本工作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>>());
    }
}
Run Code Online (Sandbox Code Playgroud)


Mau*_*rry 6

在通话中:

compiles(new HashMap<Integer, List<Integer>>());
Run Code Online (Sandbox Code Playgroud)

T 与 Integer 匹配,因此参数的类型是 a 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>());
}
Run Code Online (Sandbox Code Playgroud)

很明显,它不能接受 aHashMap<Integer, List<Integer>>作为参数。