|sealed interface Node { ... } |
In this streamlined form, |Node| may be extended only by its
nestmates. This may be suitable for many situations, but not for
all; in this case, the user may specify an explicit |permits| list:
|sealed interface Node permits FooNode, BarNode { ... } |
The two forms may not be combined; if there is a permits list, it
must list all the permitted subtypes. We can think of the simple
form as merely inferring the |permits| clause from information in
the same compilation unit.
what inferring from the same compilation unit means ?
It means: if there is no "permits" list, the compiler is allowed to
synthesize one that contains exactly the list of subtypes declared in
the same compilation unit. (Much like the way we synthesize the
NestMembers attribute.)
- it works for anonymous class declared in the same compilation unit ?
Anonymous classes are a good question. On the one hand, I can imagine
when they might be useful. On the other, it means there's no way anyone
-- including the current class, to which all the subtypes are accessible
-- will be able to switch exhaustively over them. I think we should
consider banning them, as you can always fall back to a named class.
The exhaustiveness part is important, and too easy to forget about when
you're writing the class.
(Other restrictions: classes in the permits list must be in the same
module (or if not in a module, same package and protection domain) as
the sealed type, and must be accessible to the sealed type.)
- it works for functional interface for lambda in the same compilation
unit ?
Same as for anon classes; if we ban one, we ban the other.
what exactly the permit list contains ??
(dynamic nestmate/sealed class to the rescue ?)
I don't seen an interaction with dynamic nestmates. The permits list is
an explicit list; if the dynamic class is not on the list, it can't be a
subtype, even if its in the nest. If we infer the list, we're not
recording "all nestmates", but instead inferring it to be the list of
nestmates known at compile time.
And in another compilation unit
Fun fun = x -> 2 * x; // rejected because Fun is sealed ?
Yep.
/Note:/ It might be allowable for VM support to follow in a later
version, rather than delaying the feature entirely.
Does seems to be a good idea to divorce the two. It means you can
create classes with ASM that will work for a version of the VM but not
the next one.
I think you mean "does not seem to be a good idea"? I agree, it's got
problems, such as the ones you raise. But it is a possible move we can
make, so we can keep it on the board until we have more visibility into
the cost of waiting for VM support.
*Accessibility.* Subtypes need not be as accessible as the sealed
parent. In this case, clients are not going to get the chance to
exhaustively switch over them; they’ll have to make these switches
exhaustive with a |default| clause or other total pattern. When
compiling a switch over such a sealed type, the compiler can
provide a useful error message (“I know this is a sealed type, but
I can’t provide full exhaustiveness checking here because you
can’t see all the subtypes, so you still need a default.”)
Yes !
I expect a public sealed interface with several package private
implementations to be a common pattern.
I agree qualitatively (and share your conclusion), but disagree with the
quantitative statement here ("common"). I think it will be common _for
platform and low-level library implementors_ to do this. But if the
feature is successful, this will probably be a tiny fraction of the
sealed types in the world. So yes, this is an important pattern, but I
hope it is UNcommon.
The implication is that you can not define (at least now) the
implementations inside the sealed interface because all class members
of an interface can only be public so you can not declare a
package-private implementation. I don't know exactly why private
class are not allowed in interface, it seems to be an overlook for me.
Yes, this (and fields too, even though they kind of suck in interfaces),
was largely overlooked. We are gathering a list of "gratuitous nesting
constraints", of which this is one, and at some point will bang them all
out. (Restrictions on static members in nested classes is another.
Local interfaces and enums is another. Maybe even local methods. Etc.)