为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

神不在的星期二 2023-12-13 14:33:29
首先是简单的情况(A):public class PsList implements List<Ps> { ... }别处private void doSomething(List<Ps> list) { ... }// compilesList<Ps> arrayList = new ArrayList<Ps>();doSomething(arrayList);// does not compilePsList psList = new PsList();doSomething(psList);好的。我知道我可以通过添加 ? 将其更改为“工作” 延伸如下:private void doSomething(? extends List<Ps> list) { ... }// compilesList<Ps> arrayList = new ArrayList<Ps>();doSomething(arrayList);// compilesPsList 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 PsListmap.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.javapackage com.foo;public class Ps{}第一个参数类型错误。找到:'java.lang.String,com.foo.PsList>',必需:'java.util.Map>'
查看完整描述

2 回答

?
慕标琳琳

TA贡献1830条经验 获得超9个赞

正如您在问题中途似乎意识到的那样,您的问题不在于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 Map<String, ? extends Number>。然而,以下情况可能存在:

  • 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>());

}

如果您确实有具有不同值类型的映射,则需要告诉该方法正在使用的特定类型:


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);

}


查看完整回答
反对 回复 2023-12-13
?
ABOUTYOU

TA贡献1812条经验 获得超5个赞

你绝对可以做到。这是一个例子:


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!");

    }

}

请参阅此处的演示。



查看完整回答
反对 回复 2023-12-13
  • 2 回答
  • 0 关注
  • 106 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信