It depends how far we want to take this.  If we limit this to sealed classes, we can just do as you say, copy the permitted subclasses of Sandwich into the permitted subclasses of WithCheese, possibly filtering for whether with Cheese is actually a supertype.  (If we want to capture the notion that "my implementors must also extend Sandwich", but not limit it to sealed classes, then we need a new kind of attribute to enforce this.)

We could write this as:

    sealed interface WithCheese
        permits extends Sandwich { }

and require that Sandwich be sealed.



On 4/1/2021 9:34 AM, Dan Heidinga wrote:
Seems like a reasonable proposal.

sealed interface WithCheese __refines Sandwich { }
Would `_refines` operate at compile time with javac copy/pasting the
sealed list from Sandwich into WithCheese?  An alternative is some new
classfile info to tell the VM to do the copy/paste at classload.  Both
are possible but the VM option is more expensive to implement.

--Dan

On Thu, Apr 1, 2021 at 9:24 AM Brian Goetz <brian.go...@oracle.com> wrote:
Been working with sealed classes a lot lately, and have run into a pattern that 
we might want to consider supporting more directly, which is interfaces that 
exist solely to refine a sealed class or interface.  Suppose we have:

     sealed abstract class Sandwich { }

     interface WithCheese { }
     interface Toasted { }
     interface NontraditionalStructure { }
     interface NontraditionalIngredients { }

     class Cheeseburger extends Sandwich implements WithCheese { }

     class TunaMelt extends Sandwich implements Toasted, WithCheese { }

     class Burrito extends Sandwich implements WithCheese, 
NontraditionalStructure { }

     class ChipButty extends Sandwich implements NontraditionalIngredients { }

     class PopTart extends Sandwich implements NontraditionalIngredients, 
NonTraditionalStructure { }

(see, e.g., 
https://urldefense.com/v3/__https://twitter.com/matttomic/status/859117370455060481__;!!GqivPVa7Brio!MsQV5tDsdWajfqsD3USkhms4IepmBckE_aLEMz1w6TGLcSGGhLvNWLwamCE2XSHtwg$
 , for those who don't get the joke.)

The constraint we would like to express here is that the interfaces WithCheese and 
Toasted exist to serve Sandwich, which is extension-controlled.  To capture this, we also 
seal these interfaces, but now have to redundantly enumerate their subtypes.  This is 
annoying and brittle, because we're stating it indirectly; a more direct capture of 
intent would be to say "Toasted can only be used with Sandwich."

Even if Sandwich were an interface, and then we can say "Toasted extends 
Sandwich", we still have to redundantly enumerate the subtypes.  If this game is 
about Sandwich, just having Sandwich enumerate its subtypes should be enough.

Without trying to paint the bikeshed, this is a pretty simple extension to 
sealing:

     sealed interface WithCheese __refines Sandwich { }

This says that WithCheese is sealed to either classes that extend Sandwich, or 
other interfaces that __refines Sandwich.  As a bonus, default methods in 
WithCheese can refer to methods in Sandwich, since it must always be the case 
that `this instanceof Sandwich`.

Essentially, I want to put the `permits` list on Sandwich, and have 
`WithCheese` and friends delegate their permits to `Sandwich`, rather than 
having each interface enumerate their subtypes.

Reply via email to