Well, monkey patching is even worse.
That's why in my initial comment I talked about style.
If you haven't worked with a given style, you're prone to harsh judgments and mentalities such as this one. Redefining classes/methods/interfaces at runtime works great for a lot of people ;)
Why is this even needed?
In case you're working in Java, you may not realize it, but the libraries you're using are probably doing it.
The real difference is in these cases, say for example aspect-oriented programming or dependency injection or all the other buzzwords you had to learn for Java in the last 10 years -- which require big-ass, bloated, incomplete and extremely buggy libraries -- in Ruby/Python are as natural as "Hello World!".
To put it simpler - dynamic languages allow you to treat code as data and to mutate it in however ways you like.
Catching bugs at compile-times is not just about syntax-related errors or checking if some object responds to some method call. If you really want to catch errors at compile-time, you have to dig a whole lot deeper than that, which more evolved static languages are kind of doing (mostly by forcing you to model your data-structures in a certain way, such that the language can understand its purpose and mathematically prove stuff).
But then again, Java is not a representative of static-languages, mostly because Java is a fucked-up hybrid, having all the disadvantages of static-types with none of the advantages.
When talking about static types, you really should be talking about what Haskell can do. And even Haskell has limits that dynamic languages don't, that's why it comes with its own macro-language to alleviate that.