为什么我不能在需要 List<X> 的地方使用实现 List<X> 的类?

首先是简单的情况(A):


public class PsList implements List<Ps> { ... }

别处


private void doSomething(List<Ps> list) { ... }


// compiles

List<Ps> arrayList = new ArrayList<Ps>();

doSomething(arrayList);


// does not compile

PsList psList = new PsList();

doSomething(psList);

好的。我知道我可以通过添加 ? 将其更改为“工作” 延伸如下:


private void doSomething(? extends List<Ps> list) { ... }


// compiles

List<Ps> arrayList = new ArrayList<Ps>();

doSomething(arrayList);


// compiles

PsList psList = new PsList();

doSomething(psList);

我的问题是为什么我需要这样做?对我来说完全是无稽之谈。我正在实现所期望的确切界面。我可以传递除 ArrayList 之外的其他列表类型,为什么我的不行?


生活总是比这更复杂,所以我真正的编码问题是(B):


public class PsList implements List<Ps> { ... }


private void doSomething(Map<String, ? extends List<Ps>> map, Boolean custom) {

...


// need to create a new List<Ps> of either an ArrayList<Ps> or PsList

map.put("stringValue", custom ? new PsList() : new ArrayList<Ps>());


...

}

那么,无论哪种情况,Java都会抱怨map正在等待?扩展 List 作为值。


即使我将其更改为:


List<Ps> list = new ArrayList<>();

List<Ps> psList = new PsList();

map.put("string", custom ? psList : list);

当然这不能编译:


? extends List<Ps> list = new ArrayList<>();

? extends List<Ps> psList = new PsList();

map.put("string", custom ? psList : list);

那么我应该怎么做才能让这样的事情发挥作用呢?


编辑1:好的,最小的复制品:


Ps.java


package com.foo;


public class Ps

{

}

第一个参数类型错误。找到:'java.lang.String,com.foo.PsList>',必需:'java.util.Map>'



神不在的星期二
浏览 107回答 2
2回答

慕标琳琳

正如您在问题中途似乎意识到的那样,您的问题不在于List<Ps>与PsList.问题是你不能添加到Map<String, ? extends List<Ps>>.让我们考虑一个更简单的例子:void&nbsp;doSomething(Map<String,&nbsp;?&nbsp;extends&nbsp;Number>&nbsp;map)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;map.put(String,&nbsp;Integer.valueOf(0));&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Not&nbsp;allowed.}问题在于,这Map<String, ? extends Number>并不意味着“值可以是 Number 或 Number 的任何子类”。每个通用类型对象都有一个特定的非通配符类型。意思是,不存在类型为 的 Map&nbsp;Map<String, ? extends Number>。然而,以下情况可能存在:Map<String, Integer>(仅允许整数值)Map<String, Double>(仅允许 Double 值)Map<String, Number>(允许任何 Number 子类的值)Map<String, ? extends Number>指的是可能是上述任何一个的Map&nbsp;(或者,当然,任何其他特定的 Number 子类)。编译器不知道 Map 的值是什么特定类型,但 Map 的值仍然具有特定类型,但该类型不会以任何方式使用?。因此,再次查看示例方法:void doSomething(Map<String, ? extends Number> map) {&nbsp; &nbsp; // Not allowed.&nbsp; The caller might have passed a Map<String, Double>.&nbsp; &nbsp; map.put(String, Integer.valueOf(0));&nbsp; &nbsp; // Not allowed.&nbsp; The caller might have passed a Map<String, Integer>.&nbsp; &nbsp; map.put(String, Double.valueOf(0));&nbsp; &nbsp; // Not allowed.&nbsp; The caller might have passed a Map<String, Integer>&nbsp; &nbsp; // or Map<String, Double>.&nbsp; This method has no way of knowing what the&nbsp; &nbsp; // actual restriction is.&nbsp; &nbsp; Number someNumber = generateNewNumber();&nbsp; &nbsp; map.put(String, someNumber);}事实上,您无法向类型为上限通配符的 Map 或 Collection 添加任何内容,因为无法知道这样做是否正确且安全。对于您的情况,最简单的解决方案是删除通配符:private void doSomething(Map<String, List<Ps>> map, boolean custom) {&nbsp; &nbsp; // ...&nbsp; &nbsp; map.put("stringValue", custom ? new PsList() : new ArrayList<Ps>());}如果您确实有具有不同值类型的映射,则需要告诉该方法正在使用的特定类型:private <L extends List<Ps>> void doSomething(Map<String, L> map,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Supplier<L> listCreator) {&nbsp; &nbsp; // ...&nbsp; &nbsp; map.put("stringValue", listCreator.get());}然后你可以像这样调用该方法:if (custom) {&nbsp; &nbsp; doSomething(psMap, PsList::new);} else {&nbsp; &nbsp; doSomething(listMap, ArrayList::new);}

ABOUTYOU

你绝对可以做到。这是一个例子:public class MyClass {&nbsp; &nbsp; static interface FooList<T> {}&nbsp; &nbsp; static class Ps {}&nbsp; &nbsp; static void doSomething(FooList<Ps> list) { }&nbsp; &nbsp; static class PsList implements FooList<Ps> { }&nbsp; &nbsp; public static void main(String[] args)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; FooList psList = new PsList();&nbsp; &nbsp; &nbsp; &nbsp; doSomething(psList);&nbsp; &nbsp; &nbsp; &nbsp; System.out.println("HelloWorld!");&nbsp; &nbsp; }}请参阅此处的演示。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java