Abstract class with fields implementing ValueObject
Dan Smith
daniel.smith at oracle.com
Wed Feb 23 18:35:41 UTC 2022
Fred suggested that we enumerate the whole space here. So, some cases to consider:
{ ACC_PERMITS_VALUE, not }
{ has an <init> declaration, not }
{ implements IdentityObject, not }
{ implements ValueObject, not }
"implements" here refers to both direct and indirect superinterfaces.
I'll focus on the first two, which affect the inference of superinterfaces.
(1) ACC_PERMITS_VALUE, <init> declaration
This is a class that is able to support both identity and value subclasses. It implements no extra interfaces, but can restrict its subclasses via 'implements IdentityObject' or 'implements ValueObject'.
(2) ACC_PERMITS_VALUE, no <init> declaration
The JVM infers that this class implements ValueObject, if it doesn't already. If it also implements IdentityObject, an error occurs at class load time.
(Design alternative: we could ignore the <init> declarations and treat this like case (1). In that case, the class could implement IdentityObject or be extended by identity classes without error (as long as it doesn't also implement ValueObject). But those identity subclasses couldn't declare verification-compatible <init> methods, just like subclasses of abstract classes that have no <init> methods today.)
(3) no ACC_PERMITS_VALUE, <init> declaration
The JVM infers that this class implements IdentityObject, if it doesn't already. If it also implements ValueObject, an error occurs at class load time.
(4) no ACC_PERMITS_VALUE, no <init> declaration
This is a class that effectively supports *no* subclasses. We don't infer any superinterfaces, but it can choose to implement IdentityObject or ValueObject. A value class that extends this class will fail to load. If the class doesn't implement ValueObject, an identity class that extends this class could load, but couldn't declare verification-compatible <init> methods, just like subclasses of abstract classes that have no <init> methods today.
(Design alternative: we could ignore the <init> declarations and treat this like case (3). In that case, it would be an error for the class to implement ValueObject, because it also implicitly implements IdentityObject.)
---
Spelling this out makes me feel like treating the presence of <init> methods as an inference signal may be overreaching and overcomplicating things. Today, declaring an <init> method, or not, has no direct impact on anything, other than the side-effect that you can't write verification-compatible <init> methods in your subclasses. I like the parallel between "permits identity (via <init>)" and "permits value (via flag)", but flags and <init> methods aren't really parallel constructs; in cases (2) and (4), we still "permit" identity subclasses, even if they're pretty useless.
(And it doesn't help that javac doesn't give you any way to create these <init>-free classes, so in practice they certainly don't have parallel prevalence.)
Pursuing the "design alternative" strategies would essentially collapse this down to two cases: (1) ACC_PERMITS_VALUE, no superinterfaces inferred, but various checks performed (e.g., no instance fields); and (3) no ACC_PERMITS_VALUE, IdentityObject is inferred, error if there's also an explicit 'implements ValueObject'.
How do we feel about that?
More information about the valhalla-spec-observers
mailing list