Classes final by default

I understand the reasoning provided here: "By default, all classes in Kotlin are final, which corresponds to http://www.oracle.com/technetwork/java/effectivejava-136174.html">Effective Java, Item 17: Design and document for inheritance or else prohibit it."

In practice, I find that this introduces an annoying level of friction with frameworks like Spring.  I wind up with a patchwork of manually opened classes and methods and forgetting to open a particular class is a regular source of error discovered at startup.  The language is fantastic in general, and I just don’t want little things like this to aggravate people away from it before giving it a good college try.

Is this decision still open for discussion?  At the very least – what are your thoughts about a compiler option to invert this behavior?

Jon

7 Likes

I don't think the semantics of the language should depend on compiler switches. But this problem has bitten me too, with the added quirk that the framework I used didn't even give a startup error .... it just failed to create database transactions, causing writes to be silently discarded! I lost several days debugging that one.

A better solution might be an IDE warning where if it detects you’re using some kind of class enhancer framework or a class that contains some kind of injection annotation, it can highlight the class definition in yellow if it’s not open.

Or yeah, just invert the default for everyone. I suspect it’ll cause more pain than it’s worth.

3 Likes

I'm against as in Android Development I rarely open my classes and I feel much safier with that in kotlin. Current approach is also much better in framework development. I would also like to have private members by default.

Why?
If classes were open by default and we would forget to mark class as final - troubles might happen, but when we forget to mark class as open and try to extend it - we will be notified (no trouble).

For what it's worth, my vote is for "Open by default".

Too often in Java I’ve been crippled by a library which I wanted to modify but couldn’t because the author had declared classes final. It’s not the default in Java and even then, this caused me a lot of grief. I can envision this situation becoming much worse in Kotlin with the default being closed classes.

The ability to extend classes in ways the original authors never envisioned far outweighs the (extremely rare) case where you break the class by extending it, a concern that is largely theoretical and hardly ever happens in practice in my experience.

12 Likes

For me the thing is not really classes being open or final, but functions that are more than once not designed for extension. Sometimes extension of functions break very visibly, other times it are silent killers. So I do like the safe approach here. But what I like even more is explicit choice, just like we've got with val and var. So maybe a better idea would be not to have a default at all but an explicit choice. eg:

open class Foo() closed class Bar()

This is of course very verbose and does kill some of the expressiveness of Kotlin and kind of makes the class keyword redundant.

1 Like

I'd also vote for "Open by default".

My experiance matches Cedric where final classes are rarely useful in practical terms and conversely can cause a lot of pain later on (where developers wish to extend and are blocked from doing so).  I would certainly prefer developers made the conscience design decision to ‘lock it down’ rather than the other way around.

1 Like

I understand the reasoning provided here: "By default, all classes in Kotlin are final, which corresponds to Effective Java, Item 17: Design and document for inheritance or else prohibit it."

I think the problem with this approach is that at design time full knowledge is required of what the user of the framework is going to do with it. There will always be something the user needs/wants to do the designer could not have anticipated no matter how smart s/he was. The idea of inheritance is that you can ovewrite inherited methods. If that is not possible there is no reason for inheritance as everything else can be accomplished with delegation for which Kotlin has some very nice support. So I also belong in the camp that prefers open by default.

– Oliver

3 Likes

I'm in favour of closed by default. In my experience it's been extremely rare that I've wanted to work around a problem in a library by overriding. And the thought of the design of Kotlin being influenced by Spring makes me sad. IMO the occassional inconvenience of closed by default is worth it.

2 Likes

I would also vote for ‘Open by default’. I just get started using Kotlin for Android development. and it is annoying to mark every class as open. I already know about the effective Java item 17, but I always like to mark classes as final after I complete some module. and what is the need for ‘sealed’ keyword here? Kotlin is like too many annotations and keywords to do certain things. It looks sometimes weird language.

1 Like

Not that I think that the votes here would result in a change, but I’d vote for “final / closed by default” (the status quo). I’ve seen way to many abuse of inheritance. If someone wants an open class, he should go the hard way and type the word open! :wink:

Polls are implemented in discourse.

I would prefer to have classes:

  • Open by default
  • Final by default
0 voters

It’s same for the visibility of methods/functions but by default its public.

For a pragmatic point of view I prefer Open by default (spring, …).

An open class is not like a nullable reference. It’s not going to come back and bite you with an exception or boilerplate code.

An open class is also not entirely like a public method. It’s not going to pollute your users’ IDE code completion with methods they shouldn’t care about or use.

Closing a class is a small case of control freakery. You already know that few people will have a desire to extend it, and certainly it won’t happen by accident. Yet, you want to make dead sure they won’t.

The legitimate reason to close a class is for security, and that may be a strong enough reason to close all classes by default.

Yet a more common, though not entirely legitimate, reason is to suspect your coworkers or users of being imbiciles who cannot be trusted. (Ok, to be fair, for certain projects with a complicated type heirarchy, there may be a similar argument as for public methods: coworkers may be confused on where the extension points are, and would see a benefit in the IDE’s list of open classes.)

A third, completely illegitimate, reason is to simply ape C++ and C#, which made methods non-virtual by default in large part due to a belief that it would increase performance. Well, HotSpot showed them.

In sum, I don’t think the points against outweigh the frequent and major benefit of the freedom of open-by-default classes.

7 Likes

We need to address this point head-on.

How would we re-implement Spring (or Hibernate or Mockito or all other popular frameworks) in Kotlin? What techniques would be used?

A good compromise would be having three levels:

  • closed (final in JVM)
  • (default) don’t-override-me (not final, and can be overriden in Kotlin if desired, perhaps with extra keyword)
  • i’m-designed-to-be-overriden

The same should apply to methods.

  • private
  • protected
  • i’m-designed-to-be-overriden (virtual in C#)
1 Like

I don’t see how this is a good compromise. If it’s possible to override something, people will be overriding it. The extra keyword will be nothing but annoying ceremony.

4 Likes

It’s always possible to override a class even if the designer didn’t intend it. Just flip the ACC_FINAL bit in the class file and away you go (or edit the source and recompile). Inconvenient? Sure, but it can be done, and compared to the costs of the alternative hacks, that’s usually easier.

All these sorts of controls ever do outside of sandboxes is point people in the right direction. I don’t see a reason for a class to ever be final, really, not when the JVM can de-virtualise for performance by itself. All it does is cause the potential for inconvenient later on. An attribute or KDoc tag saying “This class is not designed to be overridden” is good enough.

But this gets back to my earlier post about visibilities. The amount of time people spend working around them seems out of proportion to the value delivered.

7 Likes

Again, you’re speaking from the point of view of, “we need to stop idiots.”

I am speaking from the point of view, “we need to help people.”

Java prevents accessing private members, except through reflection. Is everyone going crazy abusing this loophole? No. But on rare occasion it is of tremendous benefit. Mostly, it is well-maintained, excellent libraries that use this loophole, not ordinary, “imbicile” developers.

Unfortunately, the JVM has no loophole for forcing a derivation of a final class, which hurts, for example, Mockito.

You need to address the question head-on. How should developers, framework creators, etc, accomplish the things they’re used to in Java? Should they use interfaces everywhere in order to support mocking? I’m sure there are workarounds, but please go into the details.

6 Likes

I agree, closed by default is the way to go.

I feel that most “correct” code is walled off from mistakes. In my uses cases I very rarely run into open classes. Typically where an open class would be is an abstract class. The easiest route should produce the most correct code. As for wanting to extend some library’s classes… isn’t this what extensions and non-sugar delegation is for? (Hint: It might be nice to have sugary delegation for all types.)

You might think this means I didn’t support public by default, but I did by far. Kotlin, unlike Java, makes it easy to write “correct” code because val is so convenient, all types are objects, and thus behavior can be extended later. In my use cases I run into things I want public most of the time, and a keyword or two here or there is nothing compared to the ceremony of public everywhere. Good riddance! :sweat_smile:

1 Like

I’m not speaking from the “we need to stop idiots” view point. I’m just saying that compromises don’t help.

A language where all methods are open by default is a sensible design, as demonstrated by Java. A language where all methods are closed by default is also a sensible design, as demonstrated by C#. A design where the designers say “we really want all methods to be closed by default, but we really want all of them to sometimes be open, so we say that the method is closed unless the user says “pretty please”” is the worst of both worlds. It lacks the safety benefits of the “all closed” design, and it lacks the ease-of-use benefits of the “all open” design.

As for mocking, my personal opinion (I’m not saying this as a decision-maker; just as a developer with some experience) is that support for mocking arbitrary classes is a non-goal. I’m a firm believer in the Chicago school of TDD. Most of interaction-based tests that I have seen end up as a rephrasing the code under test where every method call is replaced with a Mockito assertion. I fail to see how such tests can give any new information about the system, and they also require updating every time the code under test is changed.

Now, of course, the use of mocking is appropriate for heavy external components such as databases. However, such components are normally abstracted through interfaces anyway.

End of rant, sorry.

8 Likes

The combination of “public by default” and “closed by default” feels a bit strange though. Java is permissive in both regards, C# (more) restrictive in both. Kotlin is permissive in one and restrictive in the other.

3 Likes