为什么我有一个方法接收带通配符的列表:
public processGenerics(List<? extends User> users){...}
但我无法以类似的方式实例化相同的List?
List<? extends User> alist = new ArrayList<? extends User>();
[已编辑,不是原始问题的一部分,但相关]为什么我不能以正常继承的方式使用集合进行转换:
List<User> users = new ArrayList<Admin>();
答案 0 :(得分:4)
您无法实例化通配实例。您只能实例化特定实例,例如:
List<? extends User> alist1 = new ArrayList<User>();
List<? extends User> alist2 = new ArrayList<SubClassOfUser>();
List<? extends User>
的含义是“在编译时类型未知,但其上限为User
的列表 - 即User
或{{1}的子类}}“
虽然可以使用通配符定义变量,但实例可能不是 - 它必须是某事物的列表
答案 1 :(得分:4)
您问题最令人困惑的方面是您必须使用泛型类型与经典原始类型进行的基础心理转换。
在Generics之前,每个变量都有一个确定的类型,例如Object。虽然您可以为其分配一个String,但仍然是一个对象的字符串,因此范例保留 - 在所有情况下,您将是一个对象的内容分配给Object var
泛型不是这样。您可能有一个List<? extends Number>
,可以从与声明的变量类型没有instanceof
关系的一系列类型中分配,但只能满足某个模式。该模式由通配符描述。
因此,为了在推理泛型变量类型时让生活更轻松,您需要放弃简单而舒适的明确类型概念,并根据这些“类型模式”进行思考。
至于问题的第二部分:List<User>
和List<Admin>
是完全不相关的类型,无论User
和Admin
是否相关。这就是Generics的工作原理,并且有充分的理由。 Java不允许您将OrdinaryUser
添加到List<Admin>
,并且根据您的假设可能会发生:
List<User> users = new ArrayList<Admin>();
users.add(new OrdinaryUser()); // shouldn't be allowed!
官方术语是类型参数的泛型类型不变。最好是谷歌这个术语,因为它已经很好地涵盖。
答案 2 :(得分:3)
构造:
List<? extends User> user
表示可以容纳List<T>
实例的变量,其中T
是用户的某些未知子类型。当你实际实例化ArrayList<T>
时,你总是知道它应该保持什么类型,所以让你在那里表达这种不确定性是没有用的。
以不同的方式放置它:变量可以保存(可能)许多不同类型的对象。对象实例只有自己的一种类型。这就是变量声明必须更灵活的原因。
答案 3 :(得分:-1)