Java将参数约束为通用超类

时间:2019-06-16 11:32:33

标签: java generics type-parameter type-constraints

动机

我有一个Either<L, R>类,它表示两种类型之一或语义上不同的状态的值。在某些情况下,无论值是哪种替代方案,对其进行操作都是有价值的。

问题

我想要一个采用Consumer<T>的(非静态)方法,其中TLR的超类型,其中LR是该类的类型参数。
目前,java允许我执行以下操作:(静态实现)

public static <T, L extends T, R extends T> void collapse(Either<L,R> e, Consumer<T> op)

但是,当然,对于非静态实现,我无法对LR施加约束,因为已经为相关实例定义了约束。我需要施加在T上的那些约束,但是Java不允许我写以下内容,因为它一次只允许一个类在超类型或子类型约束中。鉴于所有类都至少共享Object作为一个公共超类型,这尤其令人沮丧,因此这些约束始终可以满足。

public void collapse(Consumer<? super L & R> op)

是否还有其他方法可以定义此约束,在更高版本的Java中允许使用此约束的任何提示,或者对为什么它将成为破坏性功能的任何解释?

2 个答案:

答案 0 :(得分:4)

在静态版本中,由于您需要使用者能够接受“ L”或“ R”类型,因此实际上并不需要这些类型变量:Either<? extends T, ? extends T> e

但是除此之外,我想说的是静态版本确实是您可以做的最好的事情。 Java只是没有特别具有表现力的类型系统。

我不认为伊兰(Eran)的回答是一个很好的解决方案,因为:

  1. 它限制了使用者的界限:您必须像预期的那样一般地使用界限,除非您仅使用Object(令人讨厌的范围),否则您总是会发现以下情况:您希望它只允许一点点;
  2. 它引入了一个额外的类型参数随处使用,即使您不需要使用使用者,也可以使用Either类型,因为您必须始终提供3个类型参数(即使如果它们是?)。第三种类型的参数感觉就像cru脚。

我看到的静态版本唯一真正的缺点是调用约定:Either.collapse(anEither, aConsumer),而不是anEither.collapse(aConsumer)。当然,前者稍微有些冗长...但是它可以满足您的要求,因此您可能只需要接受尴尬即可。

答案 1 :(得分:2)

也许您应该添加T作为类的第三个类型参数:

class Either<T, L extends T, R extends T>
{
    public void collapse(Consumer<T> op) {

    }
}