Abstract class with fields implementing ValueObject
Dan Heidinga
heidinga at redhat.com
Thu Feb 24 15:57:47 UTC 2022
On Wed, Feb 23, 2022 at 1:36 PM Dan Smith <daniel.smith at oracle.com> wrote:
>
> 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.)
I think ignoring the <init> declarations is the model we want here.
Both (1) and (2) should be treated the same by the VM - in either case
the subclasses can implement IdentityObject or ValueObject. Whether
they can be instantiated is a decision better left to other parts of
the spec (in this case, I believe verification will succeed and
resolution of the `super()` <init> call will fail).
>
> (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.
I think this should be driven purely by the presence of the
ACC_PERMITS_VALUE flag and the VM shouldn't be looking at the <init>
methods. The JVM shouldn't infer either IdentityObject or ValueObject
for this abstract class - any inference decision should be delayed to
the subclasses that extend this abstract class.
An abstract class that doesn't have the ACC_PERMITS_VALUE flag binds
tightly to the IdentityObject interface. The presence of the
ACC_PERMITS_VALUE flag delays the interface binding until we hit
either a concrete class or an abstract class without the flag.
>
> (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.
See above. No ACC_PERMITS_VALUE flag means it binds tightly to
IdentityObject and can only be subclassed by IdentityObject-compatible
classes. All value classes extending this abstract 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.
The class extending this will load but cannot be instantiated.
Verification succeeds but resolution of any super() calls in the
constructor will fail to resolve.
>
> (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.)
+1.
>
> ---
>
> 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?
>
I think this design alternative is the right strategy and more inline
with existing conventions for how the VM handles classes.
--Dan
More information about the valhalla-spec-experts
mailing list