1

I want to define a function which uses a type with multiple bounds, where one of the bounds is another type parameter. For example:

<A, R extends A & SomeInterface> R doSomething(...);

It seems (according to Intellij IDEA) this is not allowed, nor is any type with multiple bounds, where any of those bounds is a type parameter. So these are illegal:

<A, R extends A & SomeInterface> R doSomething(...);
<A, B, R extends A & B> R doSomething(...);

But these are legal:

<R extends SomeType & SomeInterface> R doSomething(...);
<A, R extends A> R doSomething(...);

The case of extending a type parameter and an interface is prohibited, but replacing the type parameter with a literal type (class, enum, or interface) is allowed. I would understand if it was not allowed to have a type parameter as bounds at all, but this is not the case. Is there something I'm missing?


In case this is an xy problem, the precise issue I am trying to solve is this:

public interface Functor<A, Self extends Functor<?, Self>>
{
  <B, SelfB extends Self & Functor<B, Self>> SelfB map(Function<A, B> f);
}

The above declaration, if it were legal, seems to provide sufficient constraints to solve this problem; ensuring that the return type is a functor of the same type, with B as its data parameter. This is a case of extending an interface and some other type determined by a type parameter.

Zoey Hewll
  • 4,788
  • 2
  • 20
  • 33

1 Answers1

1

Java has such restriction because there's no guarantee that <A, R extends A & SomeInterface> R doSomething(...) is going to work for all generic types A.

Imagine there are SomeInterface and AnotherInterface:

public interface SomeInterface {
    void foo();
}

public interface AnotherInterface {
    String foo();
}

Perhaps you already see the problem

Imagine you execute doSomething<..., AnotherInterface>(...) the R type now should have void foo() and String foo() methods at the same time, which is not possible due to signatures conflict.

Besides, I'm not sure if doSomething<..., SomeInterface>(...) case would be handled correctly: after all, an interface is not the subtype of itself

Probably there are more problems with it, though one counterexample is enough to show that general rule doesn't work

It works when you plug specific classes into generic parameters, because a compiler is able to infer whether you have conflicts or not, that's why <R extends SomeType & SomeInterface> R doSomething(...) is legal

Sergei Voitovich
  • 2,804
  • 3
  • 25
  • 33
  • 1
    One may argue that the declaration proposed by the OP could be legal and the pathological invocation that you mention in the answer would trigger a compile-time error. – Marco Apr 27 '18 at 09:11
  • In order to use such a function you would at some point have to create a value of a class that implements both those interfaces (because the generic method itself cannot create a value of a generic type), and doing so would cause a compile-time error. Therefore, the type of the invocation itself wouldn't even need to be checked in this case. – Zoey Hewll Apr 28 '18 at 06:34
  • Oops, I had forgotten that method invocation isn't the only time such a restriction (the one mentioned in the first comment) would come into play, and that without that restriction someone could make a class with unusable methods based on the class' type arguments. – Zoey Hewll Apr 28 '18 at 08:36
  • So I'm thinking, it would make the most sense to prevent assignment to type parameters that would lead to incompatible bounds. But not block generic bounds which could be assigned incompatible types. – Zoey Hewll Apr 28 '18 at 08:37
  • Also, to answer a question in your answer, the constraint of `A extends B` is compatible with the type `B`. That is to say, in the context of type bounds, `B extends B`. It is also true that `B super B`. – Zoey Hewll Apr 28 '18 at 09:26
  • 1
    @ZoeyHewll check this out https://stackoverflow.com/questions/197190/why-cant-i-use-a-type-argument-in-a-type-parameter-with-multiple-bounds/197391 There is the discussion that lasts for a decade. In short, they've come up with the same 'wrong type' idea. Moreover it was done to restrict unwanted complexity it might bring. I like this one >"... to preclude certain awkward situations coming into existence" It makes not much sense without certain examples, but having said that one can get rid of questions of his language design choices) – Sergei Voitovich Apr 30 '18 at 19:19