Proposal: generic receiver parameter

Mohamed Maaliki username11681 at gmail.com
Sun Apr 3 16:31:03 UTC 2022


Hello. I've designed multiple fluent APIs with inheritance. While on one 
hand their usage can be concise and readable, on the other their 
implementation is verbose.

Let's say that we have
```
class A {
     A withX(int x) {...}
}
class B extends A {
     B withY(int y) {...}
}
```
. `new B().withX(x).withY(y)` is erroneous because `A::withX` returns 
`A`. I see 2 solutions to this problem:
1. overriding `withX` with return type `B`; and
2. adding a "`this` type" parameter `T extends A<T>` to `A`.
Solution #1 is acceptable in small classes (which are unlikely) that 
don't change but if `A` receives new methods or is large, then the 
implementation of `B` becomes very noisy.
Solution #2 reduces noise in `B`'s implementation but it makes usage of 
`A` and its subclasses verbose instead: instantiation needs a diamond 
(`new B<>().withX(x).withY(y)`); field and parameter declarations of 
type `B` have to redundantly specify a wildcard type parameter (`B<?> 
parameter`); and such fluent methods must perform an unsafe cast to `T` 
when returning.
This API that was initially intended to be readable now hurts readability.

Since the JLS allows the receiver to be declared explicitly, the problem 
could be solved more elegantly by extending it to allow the receiver to 
be generic:
```
class A {
     <T extends A> T withX(T this, int x) {...}
}
class B extends A {
     <T extends B> T withY(T this, int y) {...}
}
```
. The compiler substitutes the type of the receiver for `T`. Now `B` can 
extend `A` without a redundant and potentially unsafe type parameter or 
having to override `withX` and their usages are clean:
```
void useB(B b) {
     doSomething(b.withX(x).withY(y));
}
```
. We can see that `T` is always assignable to its enclosing type; so we 
could eliminate the explicit upper bound: `<T> T withX(T this, int x) 
{...}`.

However, if the fluent method has multiple type parameters, then its 
invocations that explicitly supply type arguments should be allowed to 
omit the receiver type argument; so the receiver type parameter must be 
declared either first or last.

Regards.



More information about the amber-dev mailing list