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

标签 java generics

首先是简单的情况(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/

相关文章:

java - 在 spring boot 应用程序中获取 Class not Found Exception 错误

java - 为什么 Java 集合的删除方法不是通用的?

java - 通配符泛型类型和泛型类型之间未经检查的转换

java - 通用工厂类实例化

java - 如何以通用方式使用算术运算来减少原始数组?

java - 复制栈顶元素Java

java - 如何清理 HttpServletRequest 中 getCookies()、getRequestURL() 的返回值?

java - 如何在不更改代码的情况下从编译的 jar 中检索完整的异常消息?

Java 为什么我的方法 PaintComponent() 不起作用?

java - 返回具有通用输入的嵌套对象