只写集合有什么意义?

mic*_*wth 6 java generics collections

<? extends T>形成只读集合

<? super T>形成只写集合

我以某种方式明白为什么使用只读集合,例如在多线程环境中使用它(还有其他情况吗?)

但为什么要使用只写集合呢?如果您无法在某些时候读取它并使用它的值,那还有什么意义呢?我知道你可以从中获取一个对象,但这违反了类型安全。

编辑:@Thomas 链接的问题(Java 中 <? super T> 和 <? extends T> 之间的差异)确实展示了如何创建只写集合,但没有回答“为什么”你首先需要一个集合。所以它不是重复的

Hol*_*ger 9

类似的陈述

\n
\n
    \n
  • <? extends T>形成只读集合

    \n
  • \n
  • <? super T>形成只写集合

    \n
  • \n
\n
\n

只是错误的。通配符元素类型不说明任何有关读取或写入能力的信息。

\n

显示反例:

\n
static <T> void modify(List<? extends T> l) {\n    l.sort(Comparator.comparing(Object::toString));\n    l.remove(l.size() - 1);\n    Collections.swap(l, 0, l.size() - 1);\n    l.add(null);\n    duplicateFirst(l);\n}\nstatic <U> void duplicateFirst(List<U> l) {\n    U u = l.get(0);\n    l.add(u);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

显示了相当多的可能的修改List<? extends T>没有问题

\n

同样,您可以阅读List<? super T>.

\n
static <T> void read(List<? super T> l) {\n    for(var t: l) System.out.println(t);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

由 施加的使用限制? extends T? super T与 相关T。您不能获取 类型的对象T(例如,从另一个方法参数)并将其添加到List<? extends T>,因为 list\xe2\x80\x99s 实际类型可能是 的子类型T。同样,您不能假设 a 的元素List<? super T>属于 类型T,因为 list\xe2\x80\x99s 实际类型可能是 的超类型T,因此您可以做出的唯一假设是元素是 的实例Object,因为每个对象是。

\n

所以当你有这样的方法时

\n
public static <T> void copy(List<? super T> dest, List<? extends T> src)\n
Run Code Online (Sandbox Code Playgroud)\n

该方法不能(以类型安全的方式)从中获取元素dest并将其添加到其中src,但只能反过来。

\n

需要强调的是,与其他编程语言不同,Java 具有使用站点差异,因此上述两个列表之间的关系仅适用于copy声明此关系的方法。传递给此方法的列表在其整个生命周期中不必是 \xe2\x80\ Tx9d 的 \xe2\x80\x9cconsumer 和\xe2\x80\x9c Producer 。T

\n

所以你可以使用类似的方法

\n
List<Integer> first = List.of(0, 1, 2, 3, 7, 8, 9);\nList<Number> second = new ArrayList<>(Collections.nCopies(7, null));\nCollections.copy(second, first);\nList<Object> third = new ArrayList<>(Collections.nCopies(11, " x "));\nCollections.copy(third.subList(2, 9), second);\nSystem.out.println(third);\n
Run Code Online (Sandbox Code Playgroud)\n

是的,copy这是一个现实生活中的例子在线演示

\n

请注意,当列表的实际元素类型为 时,对于两次调用,列表如何second将其角色从消费者更改Integer为生产者。ObjectcopyNumber

\n

其他示例? super T

\n\n
\n

总而言之,在 Java 中,像PECS这样的规则与方法的声明相关,以确定方法本身中参数的(典型)角色。List<Integer>这提高了调用者的灵活性,因为它允许组合不同的不变类型,例如从 a 复制到 a的示例List<Number>

\n

但永远不要假设泛型类型会告诉您有关读取或写入集合的能力的任何信息。

\n


Tho*_*ger 6

请注意,“只写集合”取决于观点。

让我们编写一个将一堆数字添加到集合中的方法:

public static void addNumbers(List<? super Integer> target, int count) {
    for (int i = 0; i < count; i++) {
        target.add(i);
    }
}
Run Code Online (Sandbox Code Playgroud)

对于此方法,列表target是只写列表:该方法只能向其中添加数字,不能使用添加到列表中的值。

另一边是来电者:

public static void caller() {
    List<Number> myList = new ArrayList<>();
    addNumbers(myList, 10);
    double sum = 0;
    for (Number n: myList) {
        sum += n.doubleValue();
    }
    System.out.println(sum);
}
Run Code Online (Sandbox Code Playgroud)

此方法适用于特定列表 ( ),因此可以读取填充到其中的myList值。addNumbers

对于此方法,列表不是只写列表,对于此方法,它是普通列表。