首先是简单的情况(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
{
}
PsList.java
package com.foo;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class PsList implements List<Ps>
{
@Override
public int size()
{
return 0;
}
@Override
public boolean isEmpty()
{
return false;
}
@Override
public boolean contains(Object o)
{
return false;
}
@Override
public Iterator<Ps> iterator()
{
return null;
}
@Override
public Object[] toArray()
{
return new Object[0];
}
@Override
public <T> T[] toArray(T[] a)
{
return null;
}
@Override
public boolean add(Ps ps)
{
return false;
}
@Override
public boolean remove(Object o)
{
return false;
}
@Override
public boolean containsAll(Collection<?> c)
{
return false;
}
@Override
public boolean addAll(Collection<? extends Ps> c)
{
return false;
}
@Override
public boolean addAll(int index, Collection<? extends Ps> c)
{
return false;
}
@Override
public boolean removeAll(Collection<?> c)
{
return false;
}
@Override
public boolean retainAll(Collection<?> c)
{
return false;
}
@Override
public void clear()
{
}
@Override
public Ps get(int index)
{
return null;
}
@Override
public Ps set(int index, Ps element)
{
return null;
}
@Override
public void add(int index, Ps element)
{
}
@Override
public Ps remove(int index)
{
return null;
}
@Override
public int indexOf(Object o)
{
return 0;
}
@Override
public int lastIndexOf(Object o)
{
return 0;
}
@Override
public ListIterator<Ps> listIterator()
{
return null;
}
@Override
public ListIterator<Ps> listIterator(int index)
{
return null;
}
@Override
public List<Ps> subList(int fromIndex, int toIndex)
{
return null;
}
}
OtherService.java
package com.foo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class OtherService
{
private void doSomething(Map<String, List<Ps>> map, Boolean custom)
{
if (custom)
{
map.put("someValue", new PsList());
} else {
map.put("someValue", new ArrayList<>());
}
}
private void callDoSomethingNotCustom()
{
Map<String, List<Ps>> map = new HashMap<>();
doSomething(map, false);
}
private void callDoSomethingCustom()
{
Map<String, PsList> map = new HashMap<String, PsList>();
// map is not the right format
doSomething(map, true);
}
}
Wrong 1st argument type. Found: 'java.lang.String,com.foo.PsList>', required: 'java.util.Map>'
最佳答案
正如您在问题中途似乎意识到的那样,您的问题不在于 List<Ps>
可与 PsList
互换.
问题是您无法添加到 Map<String, ? extends List<Ps>>
.
让我们考虑一个更简单的例子:
void doSomething(Map<String, ? extends Number> map) {
map.put(String, Integer.valueOf(0)); // Not allowed.
}
问题是 Map<String, ? extends Number>
并不意味着“值可以是 Number 或 Number 的任何子类。”
每个通用类型对象都有一个特定的非通配符类型。意思是,不存在类型为 Map<String, ? extends Number>
的 Map 。但是,以下可能存在:
-
Map<String, Integer>
(仅允许整数值) -
Map<String, Double>
(仅允许 Double 值) -
Map<String, Number>
(允许任何 Number 子类的值)
Map<String, ? extends Number>
指的是一个 Map,可能是上述任何一个(或者,当然,任何其他特定的 Number 子类)。编译器不知道 Map 的值是什么特定类型,但 Map 的值仍然有一个不使用 ?
的特定类型。以任何方式。
所以,再次查看示例方法:
void doSomething(Map<String, ? extends Number> map) {
// Not allowed. The caller might have passed a Map<String, Double>.
map.put(String, Integer.valueOf(0));
// Not allowed. The caller might have passed a Map<String, Integer>.
map.put(String, Double.valueOf(0));
// Not allowed. The caller might have passed a Map<String, Integer>
// or Map<String, Double>. This method has no way of knowing what the
// actual restriction is.
Number someNumber = generateNewNumber();
map.put(String, someNumber);
}
事实上,您无法向类型为上限通配符的 Map 或 Collection 添加任何内容,因为无法知道这样做是否正确且安全。
就您而言,最简单的解决方案是删除通配符:
private void doSomething(Map<String, List<Ps>> map, boolean custom) {
// ...
map.put("stringValue", custom ? new PsList() : new ArrayList<Ps>());
}
如果您确实有具有不同值类型的 map ,则需要告诉方法正在使用的特定类型:
private <L extends List<Ps>> void doSomething(Map<String, L> map,
Supplier<L> listCreator) {
// ...
map.put("stringValue", listCreator.get());
}
然后你可以像这样调用该方法:
if (custom) {
doSomething(psMap, PsList::new);
} else {
doSomething(listMap, ArrayList::new);
}
关于java - 为什么我不能在需要 List<X> 的地方使用实现 List<X> 的类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58546086/