[sealed] SafeVarargs and sealed classes
Tagir Valeev
amaembo at gmail.com
Fri Sep 27 07:25:32 UTC 2024
Hello!
Using generic varargs in non-final (in particular, interface) methods is
still painful in Java, as one cannot annotate them as @SafeVarargs, so
every callsite has an unwanted warning about generic array creation.
Closed sealed hierarchies allow to control all the implementations and
explicitly mark all of them with @SafeVarargs. In this case, we could
improve the situation if we allow marking the parent method as well.
E.g., consider the following code:
import java.util.Optional;
public class Main {
public sealed interface Service permits ServiceImpl {
@SuppressWarnings("unchecked") // cannot use @SafeVarargs here
<W> void process(Optional<W>... params);
}
public static final class ServiceImpl implements Service {
@SafeVarargs
@Override
public final <W> void process(Optional<W>... params) {
for (Optional<W> param : params) {
param.ifPresent(System.out::println);
}
}
}
void test(Service service, ServiceImpl serviceImpl) {
// Warning: warning: [unchecked] unchecked generic array creation
service.process(Optional.of(1), Optional.of(2), Optional.empty());
// No warning thanks to SafeVarargs
serviceImpl.process(Optional.of(1), Optional.of(2), Optional.empty());
}
}
Here, we cannot use the Service::process method without an unchecked
warning about generic array creation. Which is bad, as we have closed
hierarchy and explicitly marked all the implementations as safe.
To improve the situation, I suggest the following additions to the Java
language specification:
1. Allow marking non-final, non-private methods (abstract or not) with
@SafeVarargs if they are declared inside a sealed or final (*) class /
interface
2. If a vararg method overrides / implements another vararg method marked
with SafeVarargs, then the overriding method must be marked with
SafeVarargs as well; otherwise a compilation error occurs.
3. If a non-sealed class / interface is declared, all its visible methods
are examined. If any of them is overridable and marked with SafeVarargs,
then a compilation error occurs.
(*) Extending this to final classes is not quite necessary, but it looks
logical addition and allows to omit final method modifier inside final
class.
With these rules, the code above could be rewritten as:
import java.util.Optional;
public class Main {
public sealed interface Service permits ServiceImpl {
@SafeVarargs // now allowed, as we are inside sealed interface
<W> void process(Optional<W>... params);
}
public static final class ServiceImpl implements Service {
@SafeVarargs // must be specified
@Override
public <W> void process(Optional<W>... params) { // possible to omit
'final' here
for (Optional<W> param : params) {
param.ifPresent(System.out::println);
}
}
}
void test(Service service, ServiceImpl serviceImpl) {
// No warning anymore thanks to SafeVarargs
service.process(Optional.of(1), Optional.of(2), Optional.empty());
// No warning thanks to SafeVarargs
serviceImpl.process(Optional.of(1), Optional.of(2), Optional.empty());
}
}
What do you think?
With best regards,
Tagir Valeev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20240927/8a166042/attachment.htm>
More information about the amber-spec-experts
mailing list