Yeah, I have plenty of criticisms of Go, but this is something that it got right.
For those who aren't familiar with the issue, in Java you can assign an array of a subclass to a variable declared as an array of the superclass, which leads to issues if you actually try to mutate it. Imagine if Cat and Dog both inherit from Animal, assigning a Dog[] to an Animal[] is totally valid, but then setting one of the elements to a Cat will throw an an exception.
I don't know if I disagree with you or not, but having not used a language with inheritance with a while, I also don't really miss it at all. Even if it doesn't cause problems on its own, it just doesn't feel like it provides that much that can't be provided via some other mechanism either.
For what it's worth, I'm not sure this is even something I'd consider a "notorious" problem with OOP because the solution is really simple and wouldn't break anything else if done correctly from day one: just don't allow using arrays of subtypes in a place where an array of its supertype is expected. This was already known at the time Java was designed, and I'm pretty sure the people who designed Java knew it too; they just didn't pick the right way to handle it in my opinion. There's nothing inherent about OO that makes this bug that much harder to deal with than any other language because it can happen in any paradigm with subtyping (as demonstrated by the fact that it could have been present in Go if they did allow casting arrays of interfaces in that way).
The mutability is a red herring here. It depends on what methods your object has and where the generic type resides within the signature. It just so happens that an immutable array has only one relevant "method":
get :: Int -> T
Here, T is on the right/return side, therefore an immutable array type would be covariant. But a mutable array also has a
set :: Int, T -> void
which has T on the left/parameter side and therefore requires contravariance (and you cannot have both, therefore you get invariance).
But the issue is not with mutability, since you could just as well have something like
setImmutable :: Int, T -> Array(T)
and you still wouldn't be able to make Array(T) covariant.
And for the record, it is only raw arrays in Java that have this issue, generic types like List<T> are invariant (of course you can explicitly ask for a List<? super T> if you want to only call "setter" type methods on it, or List<? extends T> for getters, aka methods where T appears only on return side).
This setImmutable would return a new instance of Array<Animal> which would not be Array<Cat> like the original, and would contain any mix of cats and dogs.
So immutability resolves the covariance problem, I think?
My main point was that mutability and covariance are two orthogonal concepts that just happen to overlap here due to function signature shape. But it seems that you are right, I cannot think of any expression that would type-check with Array<Animal> but not with Array<Cat>. ChatGPT was very unhelpful in trying to produce a counterexample, since it probably trained on too many entry level stackoverflow posts about this issue.
Interface inheritance is OK (as long as you pay attention to these issues) but implementation inheritance is a huge mess because it's effectively antithetical to modularity. Your entire class hierarchy must be understood/evolved as a whole if you use implementation inheritance, that's the only way to avoid running into severe problems with its semantics as the code changes over time. Of course that's not exactly the most common approach given that class hierarchies in many real-world codebases are large and it's just not practical to survey them as a whole.
Surely that's just contravariance, though? You can't cast []string to []any because that would allow you to write non-strings to it.