Classes final by default

It seems that everyone who dislikes Point 17: Design and Document for Inheritance or Else Prohibit It is also forgetting about Point 16: Favor Composition Over Inheritance. As long as the library builders provide an interface to work with, you can “extend” any class in the hierarchy using a Decorator or Delegate, for which Kotlin provides a first class feature.
Also note that, as the Kotlin documentation states, that other languages (such as C#) have been safely doing this for years. I’ve never heard a complaint from them.

1 Like

Not everyone. I for one favor composition over inheritance, or indeed functional composition by passing around lambdas as mentioned earlier up. For OO paradigm, I still prefer open by default. Reasons:

  • I like frameworks that can provide run-time instrumentation. Spring transaction management, security, etc.

Compile time weaving could be used, I guess, but the tools at the moment default to using cglib for concrete classes.

One could use java agents, too. There is no need to weaken the language for such concerns.

Good suggestion. Perhaps I’ll give that a try. Things I’d need to set up for that:

  • Specifying agents via gradle bootRun
  • Specifying agents in the init.d script for prod
  • Specifying agents in IntelliJ
  • Keeping the versions up to date (quasar and ApsectJ weaver)

There are too many manual steps (last time I tried), but things could be improved over time there.

It may be easier to perform the transformation in a custom classloader than to use a java agent, as that can be set up in your code and not require setting up everywhere which launches your program.

A gradle plugin which can transform the classes in a dependency (and remap them?) could also work. I am planning to write a companion to JarOpener called JarOpenerGradlePlugin which will do this

1 Like

This post in russian is about many world frameworks and technologies, based on JavaBeans spec. The most recent requirements are default constructor, class should be not final and has no final get/set methods bacause of instrumentation to be still available, i.e. inheritance (for correct instanceof) and overriding (for inject framework logic). We use in our project similar based on javassist library technique for class extending in runtime and PropertyChangeSupport injecting (we inherit classes and override setters with super.setXXX calls, but after super call we add throwing change event code). I want NOT specify open, because its a workaround of kotlin restctions, as using agent too. Kotlin’s position is “Better than in Java”, but with primitive task of standard JavaBean generation we have an problem!!! For me, more easy to create regular java class with private members and click “Generate setters and Setters” or include Lombok in project and specify get and set generation annotations without any kotlin`s or other restrictions. For now we are not using kotlin, because our environment instruments is based on POJO and JavaBeans contract, kotlin not privides it “Out of the box”, data classes hard final, for regular class we need to submit “open class” workaround.

1 Like

Its not a language’s problem, its problem of developer, that constructs this class so, that inherited class will be incorrect. It would be nice to have reflected directive in Kotlin, aka “closed class”, e.g. open by default and “closed” by manual spec.

Ugg, so many CglibAopProxy: Unable to proxy method errors popping up all over the place after trying to switch over a Spring app to Kotlin. I too find the closed-by-default rather annoying, my code is getting littered with open all over the place.

@sdavids There will be an ongoing discussion around solutions to that here. One way or another, we will provide a workaround in spring-kotlin.

Awesome, thanks for the update. It’s still a little annoying from a mocking perspective but manageable.

For mocking a framwork like JMockit could be the solution. JMockit can mock final elements without additional tools like PowerMock. Maybe JMockit could be decorated with a thin Kotlin convenience layer.

Closed by default. That was one of the main things that drew me to the language - not necessarily that it was in place, but that the consideration was there.

In java, I close everything (manually) by default and open it up only when there’s a need. Then when there’s a need, I make sure that I handle it appropriately by closing methods no one has any business overriding - or, if possible, making the class abstract, extending it with my “default” behavior, and making that default class final.

Having to manually mark the class open should make sure it’s done with good reason. Yes, tools that make proxies need classes to be open in order to work with them; in this case, I’d still say that’s an issue on either the tool’s side of things or the developer using it, not the language.

(Although I will definitely grant that only finding it out at runtime is a massive pain. It’d be awesome if there was a way to integrate something in the compilation pipeline that could at least warn you about it and say something like “Hey, Spring probably needs this class to be opened; you wanna fix that?”)

1 Like

I don’t know that much about Spring (yet), but I wonder if the following approach might be helpful. Have some annotation which you can apply to interfaces, base classes, or indeed other annotations (like @Configuration). (I’m assuming that these days you can add an annotation to something post hoc, in another, downstream project – is that right?) Its meaning would be “all implementing classes / subclasses / annotated classes must be open”. Some compile-time tool could check that for you and tell you to ask the library author to open things, get our your JarOpener, or whatever. Kotlin guidance for Spring etc. could come with a list of necessary and optional things to annotate.

Would that be workable and user-friendly enough?

I think what we are going to wind up with is a Lombok style annotation processor hack that strips finality from Spring stereotypes and configuration classes where runtime problems are almost certain to ensue… Don’t love it, but shame on Java for not providing us multiple compilation phases to do this kind of work without hackery.

Same thing for me :slight_smile:
Converted BaseEntity POJO into Kotlin and spent 30 mins to realize that id property should be open in my case, because of some magic in CrudBaseDAO:

Initially, i googled for java.lang.IllegalArgumentException: id to load is required for loading, but search results have nothing to do with the fact that I have final getters/setters for id field.

Please see for the solution to this problem.

@yole, yes, I did that. But for some unknown reason i’ve expected that spring compiler plugin will generate open modifier for my JPA entities. That’s of course not true :slight_smile:

Fixed my code by adding this setup

        <!-- Or "no-arg" -->
        <!-- Each annotation is placed on its own line -->

That was my fault.

In that section i found wrong comment for Maven setup:

        <!-- Or "kotlin-spring" for the Spring support -->

But plugin name should be spring instead of kotlin-spring. Proof:

[ERROR] Failed to execute goal org.jetbrains.kotlin:kotlin-maven-plugin:1.1.0:compile (compile) on project requisition-model: Plugin not found: kotlin-spring: java.util.NoSuchElementException
[ERROR] role: org.jetbrains.kotlin.maven.KotlinMavenPluginExtension
[ERROR] roleHint: kotlin-spring
[ERROR] -> [Help 1]

Same for no-arg plugin:

        <!-- Or "kotlin-jpa" for JPA support -->

Here should be just jpa instead of kotlin-jpa.

Thanks, we’ve fixed that.

1 Like

Some months ago I created several subclasses of some of the classses in java.util.concurrent.lock. The purposes was to collect information how many threads are waiting on some specific lock, how long do they wait, till the get holf the lock, how long do threads stay inside the lock. The whole thing collects all this data and is completely transparent. And that is because all those classes in java.util.concurrent.lock are open for subclassing. Everything could be implemented transparently, because it was always possible to override the respective method. One reason for the success of Java is that it does not try to control things that much. Too much control means lack of flexibility.