Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java constrain parameter to common superclass

Motivation

I have an Either<L, R> class, which represents a value of one of two types, or semantically different states. In some cases, it is valuable to operate on it no matter which alternative the value is.

Problem

I want a (non-static) method that takes a Consumer<T>, where T is a supertype of both L and R, where L and R are type parameters for the class.
Currently, java lets me do this: (static implementation)

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

But of course, with a non-static implementation, I can't impose constraints on L and R, because they're already defined for the instance in question. I need those constraints imposed on T instead, but java won't let me write the following because it only allows one class in a supertype or subtype constraint at once. This is especially frustrating given all classes share at least Object as a common supertype, so these constraints are always satisfiable.

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

Is there any other way to define this constraint, any hint of it being allowed in a later version of java, or any explanation of why it would be a breaking feature?

like image 774
Zoey Hewll Avatar asked Jun 16 '19 11:06

Zoey Hewll


2 Answers

In your static version, since you need the consumer to be able to accept either the "L" or the "R" type, you don't actually need those type variables: Either<? extends T, ? extends T> e.

But beyond this, I would say that your static version is really the best you can do. Java just doesn't have a particularly expressive type system.

I don't think that Eran's answer is a particularly good solution (with respect), because:

  1. it bakes in a bound on the Consumer: you have to use as general a bound as you ever expect, and unless you just use Object (which is annoyingly broad), you'll always find a case where you wish it were just a tiny bit more permissive;
  2. it introduces an extra type parameter everywhere you use an Either type, even where you don't need to use the consumer, because you have to always provide 3 type parameters (even if they are ?). A third type parameter just feels like cruft.

The only real downside I see to the static version is the slightly awkward calling convention: Either.collapse(anEither, aConsumer) as opposed to anEither.collapse(aConsumer). Sure, the former is marginally more verbose... but it does what you want, so you may just have to accept the awkwardness.

like image 103
Andy Turner Avatar answered Nov 04 '22 09:11

Andy Turner


Perhaps you should add T as a third type parameter of your class:

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

    }
}
like image 24
Eran Avatar answered Nov 04 '22 07:11

Eran