"Convenient proxy factory bean superclass for proxy factory beans that create only singletons."
Basically, the error is that when some system uses virtual function calls via an interface, what that's really saying is that: "This can be changed at runtime."
So if there's some function "void foo( IBar bar )", then what that's really saying is that "bar" can change at runtime. Literally a different implementation from call-to-call.
Is that actually required?
In 99.9% of such function calls, no: the implementation of IBar will always be the same concrete class.
To see a language that has gone to the opposite end of the spectrum, take a look at Rust. It use a lot of parametric typing, which is very flexible, but by default tends to use compile-time static types instead of run-time dynamic types.
Java never went down this path because it added parametric types very late in its design.
C# had parametric types in v2.0, which is when it started getting popular.
Rust had it from the beginning.
https://golangnews.org/2020/11/experimenting-with-go-type-pa...
Do you have any idea what they did right that Java lacked?
An interface in Go can be defined by the client code and types just need to implement the functions. In Java Interfaces need to be defined before any code can use it. And exception handling makes code hard to reason about because it introduces hidden control flow.
I think Generics don’t give you that much leverage. Without them you need to duplicate the code for each type or class, but duplicated code is much easier to work with. Yes, it’s not really dry, but copy paste and search/replace is something which increases developer productivity, fixing bugs in code with too many generics decreases it.
And one thing to keep in mind: historically a lot of interfaces in Java were necessary because Java had no lambdas. In Go functions are first class citizens and you don’t need an Interface just to dynamically call different functions.
That's an important role, especially in performance-sensitive domains, but outside of that, it never seems to work very well, which makes it a easily misused feature in every language I've used that offers it. Go's original design basically took the form of offering this for the built-in structures but not letting you roll your own, ushering you towards interfaces if you really needed it, and because Go makes interfaces convenient, it mostly works.
BTW, as an example of generics abuse, crypto++ comes to mind. It's such a huge pile of templates referencing each other and exceptions being used to control the command flow, that I was once hired as a freelancer just to link the whole thing into a static library, because the company using it was unwilling to deal with the source code, but needed it for FIPS compliance.
It's an incredible engineering feat that they managed to let you mix and match cryptographic functions and containers and storage formats as you see fit, but having a C++ stack trace with 5 abstract templates in it makes it very painful to debug if stuff goes wrong.
And maybe that's just my personal style, but to me defer is much more important than generics.
I can't even remember how often I've had to debug Java bugs where someone forgot to close a ResultSet for an SQL query... And then you leak memory so badly that restarting tomcat with a daily cron is still not enough :(
With defer, you can expect people to write the cleanup code immediately after opening something, which makes it much easier to confirm that you're not leaking resources.