通常不鼓励在Java中的返回参数中使用通用通配符类型.例如,Effective Java,第28项规定:
不要将通配符类型用作返回类型.它不会为您的用户提供额外的灵活性,而是迫使他们在客户端代码中使用通配符类型.
正确使用的通配符类型对于类的用户几乎是不可见的.它们使方法接受它们应该接受的参数并拒绝它们应该拒绝的参数.如果类的用户必须考虑通配符类型,那么类的API可能有问题.
但在某些情况下,它似乎是最佳选择,例如在下面的代码中:
import java.util.*; import java.lang.*; import java.io.*; class Container { private final Map> itemMap = new HashMap<>(); public void addItem(String key, T t) { itemMap.put(key, new Type<>(t)); } public Collection > getAllItems() { return itemMap.values(); } public static class Type { 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
是通用的.
设计需要一个返回所有项(getAllItems()
)的集合的方法.存储的项目itemMap
可以具有不同的类型 - 因此类型参数T不能移动到Container类声明.出于同样的原因,我们无法返回Collection
,因为这意味着所有项目都具有相同的类型T,而不是.根据Effective Java,这个方法或者这个类API有问题,在我看来这两者似乎都不对.
我知道之前已经问过类似的问题(例如,通用通配符类型不应该用在返回参数中),但是它们要么专注于特定的例子,这与我的略有不同,要么回答围绕着它没有的事实.有意义的是返回形式的集合,Collection extends ...>
而不是可以使用有界类型参数.这不适用于上述用例(我对其他用户不感兴趣,因为我可以看到它们如何被解决).
问题是这样 - 如果有效Java是正确的(其他来源,如SonarQube规则和Web上的各种文章),Collection
在上面的代码中返回的替代方法是什么,或者类Container的设计有什么问题?
更新:
一个略微修改/扩展的示例,以解决注释中提出的一些问题(这更接近我正在使用的实际代码):
import java.util.*; import java.lang.*; import java.io.*; class Type{ private T value; private Class clazz; public Type(T value, Class clazz) { this.value = value; this.clazz = clazz; } T getValue() { return value; } void setValue(T value) { this.value = value; } Class getItemClass() { return clazz; } } abstract class BaseContainer { protected abstract Collection > getItems(); // ... other methods } class Client { public void processItems(BaseContainer container) { Collection > items = container.getItems(); for (Type> item: items) { processItem(item); } } public void processItem(Type item) { T currentValue = item.getValue(); Class clazz = item.getItemClass(); T newValue = null; // ... calculate new value item.setValue(newValue); } }
如果BaseContainer.getItems()
声明了返回类型,Collection
我们为"流氓"客户端提供了一个漏洞,将无效类型的值放入Type的实例中.正是出于这个相同的原因List
是不一样的List
-这是很容易看到前返回,而不是后者将允许任何人把作为字符串成整数的列表(List
被然而List extends Object>
).
按照@isi建议的方法,我们可以使Type实现一些接口(非泛型),并在返回类型中使用它getItems
.我可以看到它是如何工作的.但是在真正的代码中,这将意味着一个全新的界面模仿大多数没有类型参数的泛型类型的方法,这感觉有点笨拙.它看起来像一个基本上写下Type
T = Object 的接口,它接近代码重复.在某种程度上,我认为Type>
这是避免这项工作和额外界面的便捷速记.