Java constrain parameter to common superclass
Asked Answered
A

2

7

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?

Arrester answered 16/6, 2019 at 11:32 Comment(0)
R
4

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.

Rip answered 16/6, 2019 at 12:50 Comment(4)
Oh in the case of point 1 you'd just use ? extends Consumer<? super T> as the consumer type.Arrester
But yeah point 2 is my main concern, I only want to worry about that common supertype when I'm using that particular method, and never else.Arrester
In that case, can we delete all comments following my first? Each of mine specifically state that they refer to point 1 of your answer (i.e. the critique of the other answer), but yours apparently were assuming them to be referring to your implementation. This means the entire discussion following is pointless and does not add to the answer.Arrester
@ZoeyHewll it's rather rude to refer to a discussion as "pointless". I simply misunderstood your comment. Best of luck solving your problem, but please consider your tone when asking others for help.Rip
S
2

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) {

    }
}
Silique answered 16/6, 2019 at 11:38 Comment(3)
As written in @Andy Turner 's answer, I only want to worry about the common supertype when I'm using that particular method, and never else. Including it in the class signature definitely makes that mathod work, but it makes all other uses of the class more difficult, and may even make some operations (eg transforming the contained types with a mapping operation) impossible.Arrester
@Silique as said this adds a type parameter to the class that the callers now have to worry about it, though they never should.Leyva
@AndyTurner makes sense. I just wanted to emphases this against Eran's answer, also the reason I upvoted yours.Leyva

© 2022 - 2025 — McMap. All rights reserved.