[sealed] Sealed interface as a utility class?

Remi Forax forax at univ-mlv.fr
Fri Oct 18 14:24:23 UTC 2019


Tagir,
there is an easy way to create a nonegleton, use a sealed interface with one implementation class and then remove that class :)

Rémi

----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Tagir Valeev" <amaembo at gmail.com>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Vendredi 18 Octobre 2019 16:16:54
> Objet: Re: [sealed] Sealed interface as a utility class?

> In the “things to tweak about interfaces” department, I would place a higher
> priority on aligning the restrictions on member accessibility — right now, we
> can have private methods, but not private member classes (or private fields,
> though that’s less of a motivation.)
> 
> I have mixed feelings about the proposal here.  On the one hand, it is motivated
> by the right thoughts — making it easy for users to follow a pattern to
> reliably do a common thing.  On the other, I don’t particularly like “language
> design by design pattern” — it feels too roundabout.
> 
> Stepping back, the proposal seems to really be about _instance control_.  Both
> the “singleton” and (warning, I’m gonna make up a silly word) “nonegleton”
> patterns are useful, but require workarounds to implement.  (Singleton requires
> either abuse of enums, or a private field with a public caching accessor;
> “nonegleton” requires a constructor that no code is ever intended to call.
> These are like the current workaround for sealed types, where we can have the
> effect of sealed classes if the constructor is not widely accessible.)  I share
> your distaste for code constructs that are intended to never be used, and are
> there entirely for their “side effects.”
> 
> Scala approaches the singleton pattern with the `object` abstraction; an
> `object` is a class with exactly one instance.  It is a more principled
> approach than static members, in that everything is still an object, and
> `object` can implement interfaces.  (Also, language support for singleton
> subsumes nonegleton reasonably well.)  As a syntactic bonus, you don’t have to
> say “static” when declaring members.
> 
> Singleton objects in Scala also also play a role in the implicits story, acting
> as “service providers” for things like factories.  There are some items on the
> longer-term roadmap that may have some overlap here as well.
> 
> All this said, I think there’s something missing here — most likely, something
> for managing instance control of classes and interfaces alike — but I’m not
> inclined to move it to the front of the queue just because sealed types are
> currently in play.  But I think its something worth continuing to think about.
> 
> 
>> On Oct 18, 2019, at 5:02 AM, Tagir Valeev <amaembo at gmail.com> wrote:
>> 
>> Hello!
>> 
>> A utility class (a class that has only static members) is a ubiquitous
>> Java pattern. They are used in JDK, common third-party libraries and
>> applications. Usually, it's a final class. Since Java 8 (static
>> methods were introduced in the interface) it's possible to convert a
>> utility class to an interface. There are a number of advantages:
>> 
>> - It cannot be instantiated, by interface definition. As a result, you
>> don't need to create an ugly constructor like `private MyUtils()
>> {throw new UnsupportedOperationException();}`
>> - If you care much about code coverage, now you would have very bad
>> times trying to cover this constructor (some people do this!). Declare
>> it as an interface, and the problem is gone.
>> - No need to specify `static final` on the fields -- less visual noise
>> - No need to specify `static` on nested classes -- less visual noise
>> - You cannot accidentally declare a non-static member: it will be a
>> compilation error, and IDE will highlight it immediately suggesting a
>> fix. My experience shows that people indeed sometimes mistakenly
>> declare a non-static member in a utility class and realize this only
>> on the first usage. This is frustrating (why I don't see the
>> completion option? I just added this method, I know it's there! Is my
>> IDE drunk?) and takes time to fix.
>> - You cannot accidentally declare a non-static field or a non-static
>> nested class. You simply cannot.
>> 
>> Unfortunately one may implement such an interface which is not what we
>> would like to allow. In fact, such an approach was used in early Java
>> versions (until import static was introduced in Java 5) to use
>> interfaces that declare only constants (this is a subset of utility
>> classes). Now it's considered an anti-pattern to implement such an
>> interface [1].
>> 
>> Having sealed classes we may declare such an interface as a sealed and
>> provide no implementations at all, making it effectively final.
>> Unfortunately (or not), the latest spec draft [2] explicitly disables
>> this (9.1.4):
>> 
>>> It is a compile-time error if a sealed interface has no permitted subtypes.
>> 
>> This could be worked around introducing a bogus implementor inside:
>> 
>> public sealed interface MyUtils {
>>  private final class X implements MyUtils {} // just to make a compiler happy
>>  // static members follow
>> }
>> 
>> This will undermine the first advantage, as the bogus class should
>> again have the throwing constructor. However, you still have less
>> visual noise, and you've got protection against an accidental
>> non-static member. So we may expect that people will (ab)use this
>> pattern.
>> 
>> The question is: whether should we support this or not? If yes, we
>> could lift the restriction on sealed interfaces without permits (which
>> essentially allows creating final interfaces). We could go further and
>> while allowing such interfaces we may disable any instance or default
>> methods inside (as nobody could call them anyway) which would make the
>> thing even more robust, and everybody would know that final interface
>> is a utility class.
>> 
>> If we don't support this, probably we should spell this out somewhere.
>> If somebody going to write a programmer's guide to sealed classes
>> (similar to TextBlocks one [3]), probably it's a good point to
>> mention.
>> 
>> A good alternative would be to invent a special syntax for utility
>> classes like `utlity class MyUtils {}`. This adds a few restrictions:
>> - All members are implicitly static; static modifier is allowed but
>> not necessary
>> - All fields are implicitly final, like in interfaces
>> - Constructors and instance initializers are disallowed
>> - Inheritors and instantiation is disallowed
>> - A `utility` modifier could be obtained via reflection
>> This would be a nice alternative, and there will be no reason to abuse
>> other language features.
>> 
>> With best regards,
>> Tagir Valeev.
>> 
>> [1] https://en.wikipedia.org/wiki/Constant_interface
>> [2]
>> http://cr.openjdk.java.net/~gbierman/jep360/jep360-20190830/specs/sealed-types-jls.html
> > [3] https://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v9.html


More information about the amber-spec-experts mailing list