By having two implementations of a common interface, like `ArrayList` and `LinkedList` both implementing `List`.
> And unsafe.
It seems like you're defining "safe" to be precisely what the languages you like provide, no more and no less. I can say that ML and Haskell are unsafe because they don't statically forbid erroneous behavior at runtime, like a sorting function that doesn't sort (or doesn't terminate). Java has no undefined behavior. In fact, it is completely unknown how much safer -- if at all -- is ML than Java in practice.
> If it's going to be unsafe, then I better at least get my money's worth in terms of performance, which is why low-level languages like C and Rust are the only ones worth FFI'ing to.
Of course it's a matter of specific requirements, but I think you get more than your money's worth in terms of performance in Java, and having decades of experience writing huge multi-MLOC programs in both Java and C++, I'm convinced that it takes significantly less effort to get a well-performing large Java app -- especially if it's concurrent -- than a C++ app, even though you could surpass Java's performance given considerable additional effort. In any event, I think that the success of the JVM shows that many people find supporting it to be worth it.
> Java is fully dynamic. “Type checking” in Java is basically a mandatory linter.
This is simply untrue. Java is mostly type safe. If you have a variable of type `Foo` in your program, it cannot reference an object of a type that is not `Foo` at runtime. You are right that this does not extend to generic types, but only if -- 1. you've intentionally tried to circumvent the type, or 2. you've fallen victim to an obscure bug that was found recently, and is very hard to reproduce accidentally.