I would say early OO is very different from what is practiced today. I use Kotlin mostly. It's obviously an OO language but it puts some interesting twists on it relative to earlier languages (like Java):
- classes are closed by default and you must define them as open to be even able to create a subclass. When you do, you must explicitly label things in classes that you override. This prevents, un-intential abuse of inheritance that is common in many Java frameworks. E.g. Spring has riduculously deep inheritance hierarchies. When I was still using Java I had a simple rule: any form of class extension is probably something I need to get rid off. Delegation is just preferable in my opinion. I almost always end up regretting class extension to the point where I rarely consider using it.
- Speaking of delegation, the Kotlin language designers obviously agree with this and added interface and property delegation to the language. This is just syntactic sugar but it's awesome. I can take any class and pass myMap: Map into the constructor and then add implements Map by myMap. And just like that you have extended a class but without actually extending it. I can even override some of the methods (because it implements the interface). They basically provided syntactic sugar for a common design pattern: delegation, which you should almost always favor over inheritance IMHO. Property delegation is equally powerful and you can use it to e.g. lazily initialize a property foo: String by lazy { someFunctionThatReturnsAString() }
- it encourages the use of val variables that cannot be reassigned. If you want that, you need to use var. If you define a var and don't reassign it, the compiler will warn you to use a val instead. Immutability by default is encouraged and it helps with e.g. asynchronous code and a few other things.
- it has sealed classes (and interfaces) as of a few versions ago. The advantage of those is that the hierarchy is closed after compilation. So you can't add more sub classes and this benefits the compiler doing some optimizations. Likewise, value classes are now a thing. In the rare cases I do use inheritance, I use sealed classes.
- it actually discourages class extension in favor of using extension functions and properties. This is much cleaner and does not suffer from a lot of the problems associated with inheritance. For example, you can't actually override anything this way. Extension functions are surprisingly useful and I use them a lot. They even work on type aliases or on nullable types. So you can call a function on a null value for e.g. a nullable generic type T and it's not going to trigger a null pointer exception when you do that. More languages should add this. This just removes a lot of use cases where you might have used inheritance or interfaces in the past. This also removes a lot of the need for multiple inheritance; which is problematic in the few languages that still support that.
- having default values on parameters in functions means that you rarely have more than 1 constructor for classes. You can add more constructors, but it's just not something you'd need often and certainly not to support different combinations of properties. Constructors have no body either. All that happens is assigning properties. This enforces the sane rule that constructors must do no work. If you need to do work on class creation, you add an init function.
I'm sure some language designers have plenty of nits to pick with Kotlin. But for me it's a very pragmatic language that mostly manages to nudge people to do the right things while providing them with a lot of convenience. Scala people tend to look down on it for example and that language does have some interesting features. But then I find most Scala code to be utterly unreadable because. Purity has a price, I guess. And of course many Scala coders consider its OO legacy to be somewhat of a mistake apparently.