当前位置: 首页 > 知识库问答 >
问题:

何时使用通用方法,何时使用通配符?

乜业
2023-03-14

我正在阅读OracleDocGenericmethod中的泛型方法。当它说什么时候使用通配符和什么时候使用泛型方法时,我对比较感到很困惑。引用文档。

interface Collection<E> {
    public boolean containsAll(Collection<?> c);
    public boolean addAll(Collection<? extends E> c);
}

我们本可以在这里使用通用方法:

interface Collection<E> {
    public <T> boolean containsAll(Collection<T> c);
    public <T extends E> boolean addAll(Collection<T> c);
    // Hey, type variables can have bounds too!
}

[...]这告诉我们类型参数正被用于多态;它的唯一作用是允许在不同的调用站点使用各种实际的参数类型。如果是这样的话,应该使用通配符。通配符旨在支持灵活的子类型,这就是我们在这里试图表达的。

我们不觉得像《代码》(Collection)这样的通配符吗

它说,继续前进,

泛型方法允许使用类型参数来表示方法的一个或多个参数类型和/或其返回类型之间的依赖关系。如果没有这种依赖关系,则不应该使用泛型方法。

这是什么意思?

他们提出了一个例子

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src) {
    ...
}

[…]

我们本可以用另一种方式编写此方法的签名,而完全不使用通配符:

class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src) {
    ...
}

该文档不鼓励使用第二个声明,并促进使用第一个语法?第一个和第二个声明有什么区别?两者似乎都在做同样的事情?

有人能照亮这个区域吗。

共有3个答案

彭礼骞
2023-03-14

在第一个问题中:这意味着如果参数的类型和方法的返回类型之间存在关系,那么使用泛型。

例如:

public <T> T giveMeMaximum(Collection<T> items);
public <T> Collection<T> applyFilter(Collection<T> items);

在这里,您将根据特定标准提取一些T。如果T是Long你的方法将返回Long集合

当情况并非如此时,您可以使用通配符类型:

public int count(Collection<?> items);
public boolean containsDuplicate(Collection<?> items);

在这两个示例中,无论集合中的项的类型如何,返回类型都将是intboolean

在你的例子中:

interface Collection<E> {
    public boolean containsAll(Collection<?> c);
    public boolean addAll(Collection<? extends E> c);
}

这两个函数将返回一个布尔值,无论集合中的项的类型如何。在第二种情况下,它仅限于E的子类的实例。

第二个问题:

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src) {
    ...
}

第一个代码允许您传递异类列表

如果你有:

interface Fruit{}

class Apple implements Fruit{}
class Pear implements Fruit{}
class Tomato implements Fruit{}

你可以做的

List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
basket.add(new Apple());
basket.add(new Pear());
basket.add(new Tomato());
List<Fruit> fridge = new ArrayList<Fruit>(); 

Collections.copy(fridge, basket);// works 

另一方面

class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src) {
    ...
}

约束列表

List<Apple> basket = new ArrayList<Apple>();
basket.add(new Apple());
basket.add(new Apple());
basket.add(new Apple());
List<Fruit> fridge = new ArrayList<Fruit>();

Collections.copy(fridge, basket); /* works since the basket is defined as a List of apples and not a list of some fruits. */

徐卓
2023-03-14

考虑下面的示例,从下面的詹姆斯·高斯林第四版的java编程中我们想合并2个SunLyLink队列:

public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
    // merge s element into d
}

public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
        // merge s element into d
}

上述两种方法具有相同的功能。那么哪个更可取呢?答案是第二个。用作者自己的话说:

“一般规则是尽可能使用通配符,因为使用通配符的代码通常比使用多个类型参数的代码更可读。当决定是否需要类型变量时,问问自己该类型变量是否用于关联两个或多个参数,或者用于关联参数类型与返回类型。如果答案是否定的,那么通配符就足够了。"

注意:书中只给出了第二种方法,类型参数名称是S而不是“T”。书中没有第一种方法。

秋飞鸾
2023-03-14

在某些地方,通配符和类型参数可以做同样的事情。但也有一些地方需要使用类型参数。

  1. 如果要在不同类型的方法参数上强制执行某种关系,则不能使用通配符,必须使用类型参数

以您的方法为例,假设您希望确保传递给Copp()方法的src列表应该具有相同的参数化类型,您可以使用如下类型参数来实现:

public static <T extends Number> void copy(List<T> dest, List<T> src)

在这里,您可以确保destsrc对于List具有相同的参数化类型。因此,将元素从src复制到dest是安全的。

但是,如果您继续更改方法以使用通配符:

public static void copy(List<? extends Number> dest, List<? extends Number> src)

它不会像预期的那样工作。在第二种情况下,您可以通过List

使用通配符和类型参数之间的其他一些区别是:

>

public void print(List<? super Integer> list)  // OK

但不能使用类型参数:

 public <T super Integer> void print(List<T> list)  // Won't compile

参考资料:

  • Angelika Langer的Java通用常见问题解答

 类似资料:
  • 问题内容: 我正在从OracleDocGenericMethod中阅读有关泛型方法的信息。当比较指出何时使用通配符以及何时使用通用方法时,我对此感到非常困惑。引用文档。 我们可以在这里使用通用方法: […]这告诉我们类型参数正在用于多态。它的唯一作用是允许在不同的调用站点使用各种实际的参数类型。在这种情况下,应使用通配符。通配符旨在支持灵活的子类型化,这就是我们在此要表达的内容。 我们难道不认为像

  • 问题内容: 这是来自 HeadFirst Java的 :(第575页) 这个: 做与此相同的事情: 所以这是我的问题:如果它们完全相同,我们为什么不写 要么 另外,什么时候使用?是有用的?而不是使用泛型的方法声明(如上所述)中的T或用于类声明?有什么好处? 问题答案: 之间的最大区别 和 是在前一种方法中,您可以在方法中将“ T”作为给出的具体类。在第二种方法中,您无法执行此操作。 这里有一个更复

  • 问题内容: 这听起来很基础…有人可以解释该方法的使用以及何时有效使用此方法吗? 在Google上进行了搜索,但找不到任何好的资源。 问题答案: 在大多数语言中,或者等效方法仅保证对象可以用文本表示。 这对于日志记录,调试或任何其他需要以字符串形式呈现遇到的任何对象的情况特别有用。 对象通常实现自定义行为,以便该方法实际上告诉您有关对象实例的信息。例如,一个类可能会覆盖它以返回“姓氏,名字”,而一个

  • 我正在用Spring web-flux和Reactor一起使用,对我来说,不清楚RestController方法何时返回

  • 问题内容: 我有一个返回定义为的方法: 我不清楚此方法的实际实现,但是当我尝试这样做时: 我收到以下编译时错误消息: Map类型的put(String,capture#9-of?)方法不适用于参数(String,String) 问题是什么?是什么类型的吗? 提前致谢。 问题答案: 通配符的意思是“值类型参数可以是任何东西”- 并不 意味着“您可以像使用任何想要的东西一样使用它”。换句话说,a 作为

  • 我在想什么时候使用静态方法?假设我有一个类有几个getter和setter,一两个方法,并且我希望这些方法只能在类的实例对象上调用。这是否意味着我应该使用静态方法? 示例: ...或: 我很困惑!