> even Java doesn't give you a way to specify "subclasses may read but not write this field", for exampleI think I know what you mean, but you can easily define a private field with a protected get method which amounts to the same thing.
In Kotlin there's no distinction between properties and fields, so you can write:
open class Foo {
var thing: String
private set
protected get
}
to express that.
> in practice they're rarely used
I'm not sure I agree. Most Java libraries use it, and it seems to work fine. Mobile UI has used it for many years and that's an absolute ton of code. Game engines use it, games are a huge chunk of the software industry. People would use it a lot more if the web worked differently or if we had better non-web distribution systems on the desktop.
> if subclasses can only use extension points that were deliberately exposed then you're probably not gaining much flexibility over what you could do with composition.
You can certainly do everything that inheritance allows without support for it in the language, but that doesn't say much. You can also do OOP in C, people do, but it's generally more convenient to have compiler support. Used properly, inheritance can certainly lead to much more convenient APIs and code without being prone to misuse.
As an example, one of the issues I have with Jetpack Compose is that if you want to customize the controls in any way you have to wrap them. There's no way to say "I want a MyButton that's the same as Button except in this one aspect", because OOP is gone. You have to define a totally new MyButton function/composable that delegates to Button. In JS it's just about tolerable due to the dynamic typing and related features (e.g. spread operator), but in Kotlin it doesn't work despite relying on compiler plugins that change the language's core semantics. You end up with huge screenfuls of boilerplate which are just forwarding function arguments from one place to another, and if you upgrade to a new Compose do you get the new features automatically? No! Due to its browser heritage React conflates widget libraries with theming, so you've had to wrap all the widgets to get anything beyond the basic Material Design look, and that's manually generated boilerplate so you'll have to go and plumb new properties through by hand. There's also no nice way for the authors of Button to define properties that are only intended for customizers vs users because there's no concrete notion of customization at all. Ugh.
Now compare to an OOP toolkit:
class MyButton extends Button {
...
}
You upgrade the base libraries and MyButton now has the new features automatically, no code changes required.
> knowing that a given class or method is final means that you can optimise that class or method much more aggressively
Virtual method calls are treated as final when possible in good language VMs anyway, so it makes no difference. They're only emitted as virtual when there are actually multiple receivers observed at that specific call site, which would have to be implemented as a switch{} in composition, so again, you don't gain anything but do lose language convenience.