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.go...@oracle.com> > À: "Tagir Valeev" <amae...@gmail.com> > Cc: "amber-spec-experts" <amber-spec-experts@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 <amae...@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