java - 在返回参数中使用通用通配符类型

标签 java generics bounded-wildcard

通常不鼓励在 Java 中的返回参数中使用通用通配符类型。例如 Effective Java,第 28 条指出:

Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code.

Properly used, wildcard types are nearly invisible to users of a class. They cause methods to accept the parameters they should accept and reject those they should reject. If the user of a class has to think about wildcard types, there is probably something wrong with the class’s API.

但是在某些情况下它似乎是最佳选择,例如在下面的代码中:

import java.util.*;
import java.lang.*;
import java.io.*;

class Container
{
    private final Map<String, Type<?>> itemMap = new HashMap<>();

    public <T> void addItem(String key, T t) {
        itemMap.put(key, new Type<>(t));
    }

    public Collection<Type<?>> getAllItems() {
        return itemMap.values();
    }

    public static class Type<T> {
        private final T value;
        public Type(T value) {
            this.value = value;
        }
        @Override
        public String toString() {
            return "Type(" + value + ")";
        }
    }

    public static void main (String[] args)
    {
        Container c = new Container();
        c.addItem("double", 1.0);
        c.addItem("string", "abc");
        System.out.println(c.getAllItems());
    }
}

这个例子当然被简化了,让我们假设有一个真正的原因 Type<T>通用。

返回所有项目集合的方法 (getAllItems()) 是设计所必需的。存储在 itemMap 中的项目可以有不同的类型 - 因此类型参数 T 不能向上移动到 Container 类声明。出于同样的原因,我们不能返回 Collection<Type<T>>因为这意味着所有项目都具有相同的类型 T 而它们没有。根据 Effective Java 的说法,此方法或此类 API 有问题,在我看来这两者似乎都不是真的。

我知道以前在这里问过类似的问题(例如 Generic wildcard types should not be used in return parameters ),但他们要么专注于特定示例,这与我的略有不同,要么围绕返回没有意义的事实展开表格集合 Collection<? extends ...>当可以使用有界类型参数时。这不适用于上述用例(我对其他用例不感兴趣,因为我知道如何解决它们)。

问题是 - 如果 Effective Java 是正确的(其他来源如 SonarQube 规则和网络上的各种文章也是如此),返回 Collection<Type<?>> 的替代方法是什么?在上面的代码中,或者 Container 类的设计有什么问题?

更新:

一个略微修改/扩展的示例,用于解决评论中提出的一些问题(这更接近我正在使用的真实代码):

import java.util.*;
import java.lang.*;
import java.io.*;

class Type<T> {
    private T value;
    private Class<T> clazz;

    public Type(T value, Class<T> clazz) {
        this.value = value;
        this.clazz = clazz;
    }
    T getValue() {
        return value;
    }
    void setValue(T value) {
        this.value = value;
    }
    Class<T> getItemClass() {
        return clazz;
    }
}

abstract class BaseContainer
{
    protected abstract Collection<Type<?>> getItems();
    // ... other methods
}

class Client {

    public void processItems(BaseContainer container) {
        Collection<Type<?>> items = container.getItems();
        for (Type<?> item: items) {
            processItem(item);
        }
    }

    public <T> void processItem(Type<T> item) {
        T currentValue = item.getValue();
        Class<T> clazz = item.getItemClass();
        T newValue = null;
        // ... calculate new value
        item.setValue(newValue);
    }
}

如果BaseContainer.getItems()返回类型声明为 Collection<Type<Object>>我们为“流氓”客户端提供了一个漏洞,可以将无效类型的值放入 Type 的实例中。出于同样的原因 List<Object>List<Integer> 不同- 很容易看出返回前者而不是后者如何允许任何人将 String 放入整数列表中( List<Integer> 但是 List<? extends Object> )。

按照@isi 建议的方法,我们可以让 Type 实现一些接口(interface)(非泛型)并在 getItems 的返回类型中使用它反而。我可以看到它是如何工作的。但在实际代码中,这将意味着一个全新的接口(interface)模仿大多数没有类型参数的泛型类型的方法,这感觉有些笨拙。它看起来也像是基本上写下了 Type<T> 的接口(interface)。使用 T = Object,这接近于代码重复。在某种程度上我看到了Type<?>作为避免这项工作和额外接口(interface)的便捷简写。

最佳答案

如果您确实打算存储绝对任何 类型的对象,那么请使用 Object。当您明确不想检查类型时,无需添加类型检查糖。

关于java - 在返回参数中使用通用通配符类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34641441/

相关文章:

java - 识别内置功能接口(interface)

java - Java 中的下限是排他性的还是包容性的?

java - 什么是 PECS(生产者扩展消费者 super )?

java - Java中的整数分区

java - ActiveMQ分布式事务+扩展

java - 为什么我会遇到通信问题...当我单击在前一个 UI 中单击链接后创建的 UI 中的按钮时?

java - 默认情况下包含 Jackson JsonView 属性

java - 如何在没有 getKey 方法的情况下从 Java 中的对象列表创建 map 列表?

c# - 使用泛型的字典

java - 从 <? 中检索子类型延伸动物>