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?

Reply via email to