T扩展U的用途?

2022-09-02 21:42:58

最近我遇到了一个类似于此定义的方法,我并不完全理解它的用法:

public static <T, U extends T> T foo(U u) { ... }

示例用法可能如下所示:

// Baz is just the containing class of foo()
Number n = Baz.foo(1);

其中推断为 和 (可能) 到 。但是,当这优于例如此方法定义时,我无法理解:TNumberUInteger

public static <T> T bar(T t) { ... }

如果我这样称呼它:

Number n = Baz.bar(2);

代码仍然有效。 被推断为或(不知道参数类型是否,在此示例中优先于调用站点返回类型TNumberIntegerIntegerNumber)

我已经阅读了这些问题:12,但我仍然不知道具有2个参数的第一种方法是否比只有一个泛型的第二种方法有任何优势。


答案 1

我认为,实际上,只有当方法的类型参数显示为作为方法签名一部分的参数化类型的类型参数时,这有意义。

(至少,我无法很快想出一个真正有意义的例子

在您链接到的问题中也是如此,其中方法类型参数用作类中的类型参数。AutoBean


一个小更新:

根据问题和其他答案中的讨论,这个问题的核心可能是对类型参数使用方式的误解。因此,这个问题可以被认为是<T的含义的副本,U在java函数声明中扩展了T>,但希望有人会认为这个答案是有帮助的。

最后,使用模式的原因可以在参数化类型的继承关系中看到,这在细节上可能相当复杂。举个例子,举例说明最相关的一点:A不是.<T, U extends T>List<Integer>List<Number>


下面是一个示例,说明它可以在哪些方面发挥作用。它包含一个“微不足道”的实现,它总是有效的(据我所知,它没有意义)。但是,当类型参数以及方法参数和返回类型的类型参数时,类型绑定变得相关。有了 ,就可以返回一个以超类型作为类型参数的类型。否则,您不能,如示例所示:TUT extends U// Does not work

import java.util.ArrayList;
import java.util.List;

public class SupertypeMethod {
    public static void main(String[] args) {

        Integer integer = null;
        Number number = null;

        List<Number> numberList = null;
        List<Integer> integerList = null;

        // Always works:
        integer = fooTrivial(integer);
        number = fooTrivial(number);
        number = fooTrivial(integer);

        numberList = withList(numberList);
        //numberList = withList(integerList); // Does not work

        // Both work:
        numberList = withListAndBound(numberList);
        numberList = withListAndBound(integerList);
    }

    public static <T, U extends T> T fooTrivial(U u) {
        return u;
    }

    public static <T, U extends T> List<T> withListAndBound(List<U> u) {
        List<T> result = new ArrayList<T>();
        result.add(u.get(0));
        return result;
    }

    public static <T> List<T> withList(List<T> u) {
        List<T> result = new ArrayList<T>();
        result.add(u.get(0));
        return result;
    }

}

(当然,这看起来有点人为的,但我认为人们可以想象这实际上有意义的场景)


答案 2

当您想要返回超类型时,这很方便;就像您在示例中所示的那样。

你取 a 作为输入并返回 a - 这是 ; 的超类型声明这一点的另一种方式是 - 但这在java中是不合法的。UTUT super U

这应该是我实际意思的一个例子。假设一个非常简单的类,如:

static class Holder<T> {

    private final T t;

    public Holder(T t) {
        this.t = t;
    }

    public <U super T> U whenNull(U whenNull){
        return t == null ? whenNull : t;
    }
}

定义的方法不会编译,这在java中是不允许的。whenNullU super T

相反,您可以添加另一个类型参数并反转类型:

static class Holder<U, T extends U> {

    private final T t;

    public Holder(T t) {
        this.t = t;
    }

    public U whenNull(U whenNull) {
        return t == null ? whenNull : t;
    }
}

用法是:

Holder<Number, Integer> n = new Holder<>(null);
Number num = n.whenNull(22D);

这允许返回超类型;但它看起来很奇怪。我们在类声明中添加了另一个类型。

我们可以诉诸于:

static class Holder<T> {

    private final T t;

    public Holder(T t) {
        this.t = t;
    }

    public static <U, T extends U> U whenNull(U whenNull, Holder<T> holder) {
        return holder.t == null ? whenNull : holder.t;
    }
}

甚至使此方法保持静态。

对于现有限制,您可以尝试执行以下操作:

Optional.ofNullable(<SomeSubTypeThatIsNull>)
        .orElse(<SomeSuperType>)

推荐