Open HosseinYousefi opened 3 weeks ago
I recommend doing:
class Foo<T extends Object> {
T foo() => ...
// use T? everywhere being nullable is allowed.
}
Even if we had a !
operator that could be applied to type variables, it would not be able to make every type non-nullable.
For example Foo<FutureOr<int?>>
would have a foo()
that returns NonNull(FutureOr<int?>
) which is FutureOr<int?>
again.
That may not be a problem for the cases where it does work.
The !
would be a general operator on types. You probably can't write int?!
, but only because it's redundant, so the only real use will be on type variables. (Again FutureOr<T>!
will be FutureOr<T>
, not FutureOr<T!>
.)
I recommend doing:
class Foo<T extends Object> { T foo() => ... // use T? everywhere being nullable is allowed. }
That's not an accurate representation. Foo<String>
and Foo<String?>
might indeed be different types. To truly differentiate them I need to add a FooOfNullable<T extends Object>
and Foo<T extends Object> extends FooOfNullable<T>
and override the methods previously defined as T?
to now return T
. But this is ugly and for K
type parameters, you'd need 2^K
different classes!
It's not the same, and nothing is because the functionality your asking for doesn't exist. I often find that the rewrite gives a better design, because the type T!
isn't what is really needed. (If T
is generic in a way that allows a user to include null
in it's values, why does the API remove null
. That's not being generic over the type T
!)
Looking at it again, if the construct is going to work, the !
probably has to be reified. The static and runtime type systems both contain types of the shape T!
, just like they contain types of the shape T?
.
Normalization would convert T!!
and T?!
to Norm(T!
), Null!
to Never
, and T!
to Object
if T
is a supertype of Object
and to T
if T
is a subtype of Object
.
(And T!?
to Norm(T?
).)
Subtyping would treat T!
like the intersection type T & Object
(which it is, it's the dual of T?
being the union type T | Null
, making a type non-nullable where ?
makes it nullable).
The interface signature of T!
is the interface signature of NonNull(T
).
It introduces yet another special-cased type, like the union types ?
and Future Or
. It does fit with the ?
type, it's not completely arbitrary.
We can currently have a type parameter like
class Foo<T extends Object> {}
and specify a method to returnT?
:However we can't do this in the other way, meaning we have a
T extends Object?
and we want to specify a method to return a non-nullable version ofT
, I propose we add aT!
variation so we can have:This is useful in the context of Java interop where the type parameter itself can be nullable but a single method be annotated with
@NonNull
.