首先是简单情况(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以外的其他List类型的原因,为什么不可以?
生活总是比这复杂,所以我真正的编码问题是(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);
}
}
第一个参数类型错误。找到:“ java.lang.String,com.foo.PsList>”,必需:“ java.util.Map>”
答案 0 :(得分:1)
您似乎已经意识到问题的中途,您的问题并不是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>
是指可能是以上任意一个(或者,当然,任何其他特定Number子类)的Map。编译器不知道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>());
}
如果您的地图确实具有不同的值类型,则需要告诉方法所使用的特定类型:
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);
}
答案 1 :(得分:0)
您绝对可以做到。这是一个示例:
public class MyClass {
static interface FooList<T> {}
static class Ps {}
static void doSomething(FooList<Ps> list) { }
static class PsList implements FooList<Ps> { }
public static void main(String[] args)
{
FooList psList = new PsList();
doSomething(psList);
System.out.println("HelloWorld!");
}
}
请参见演示here。