Reading this just reminds me of all the terrible things about Java. The lack of reasonable default string representations, comparison and hashing methods, etc. are all glaring mistakes in the language design that have wasted huge amounts of time for millions of programmers. It's as though Java programmers are so deep in the grips of Stockholm Syndrome that ordinary, sensible behaviour seems amazing and "cool".
This is similar to the situation with design patterns. When design patterns came along, Java folks talked about them as though they were a wonderful new invention. But they only exist because Java is so clumsy that it needed crutches to do things that had been easy and natural in other languages for years; someone merely came along and gave the crutches names.
Which design patterns are you thinking of and which languages had them? The original gang of four Design Patterns book is filled with C++ examples, most likely because almost nobody was using Java in 1995 outside of a few people at Sun.
More generally comparison and hashing methods seem like small potatoes compared to locking primitives, portability, and a large, well-documented class library that have made Java ideal for a very large fraction of business applications. There are many things to criticize in Java but the points you list don't seem to be especially important compared, say, to the relatively steep learning curve/complexity vs languages like modern Javascript.
I'm not saying Java doesn't have other strengths. It certainly does! It also has stupid weaknesses.
Many of the popular design patterns are simply lambda. If you have first-class functions, they are trivial. But Java doesn't have first-class functions; instead you have to write a giant thing that looks like a class, and because that feels like a lot of work, it seems to deserve a fancy name like Observer or Strategy. But it's all fabricated work; the only reason you have to do it is that the language has this stupid weakness.
It's as though you were working in a language that didn't have lists. And one day you realize, hey, it's useful to be able to manipulate variable-length collections of things. So you look at a problem and say "Aha, this is exactly the place that we should use a Collection design pattern" and you go write the entire implementation of lists from scratch and feel satisfied that you did a good day's work. You tell all the programmers you know that there is this great design pattern called a "Collection" that they should all start using. You write books about it. You give workshops. You get really good at writing list implementations, because every time you need one you implement a new list from scratch.
When you tell a Python programmer this, she looks at you like you have lost your mind, writes a function that takes a list, solves the problem in 5 minutes, and gets on with her day.
C++ circa 1995 was a lot more like Java with manual memory management, really. Templates were a rare sight, STL was not yet a thing, and it was still considered an impure OO language rather than multi-paradigm.
These were also closer to Java libraries of late 90s in design. Even ATL mostly used templates to optimize (using the curiously recurring template pattern, usually).
Qt was also born in that era, and look at how strikingly similar its standard library is to Java's in many cases, and how unlike STL and Boost.
> When design patterns came along, Java folks talked about them as though they
were a wonderful new invention. But they only exist because Java is so
clumsy that it needed crutches to do things that had been easy and natural
in other languages for years; someone merely came along and gave the
crutches names.
Design patterns are a useful way of thinking about any sort of design, not
even just in programming. Novels and paintings have them, too, so does your
favourite language. There will always emerge from experience particular ways
of using your medium, and having names for those patterns can actually be
pretty useful for thinking about them and structuring them into a good design
(like how SICP talks about knowing a spirit's name gives you power over it).
You can't avoid using design patterns; someone just might not have come
along and named them yet, or you might not be aware of them.
The GoF book itself actually discusses this in the first chapter a bit, here's
a relevant part:
Point of view affects one's interpretation of what is and isn't a pattern. One
person's pattern can be another person's primitive building block. For this
book we have concentrated on patterns at a certain level of abstraction. [...]
Our patterns assume Smalltalk/C++-level language features, and that choice
determines what can and cannot be implemented easily. If we assumed procedural
languages, we might have included design patterns called "Inheritance,"
"Encapsulation," and "Polymorphism." Similarly, some of our patterns are
supported directly by the less common object-oriented languages. CLOS has
multi-methods, for example, which lessen the need for a pattern such as
Visitor.
Not to mention languages like golang that decided not to learn from previous mistakes (like the billion dollar mistake by including null in the language)
You either like strongly typed object oriented langs or you don't. If you do, then a bit of verbosity is really nothing. If there are actual lang improvements beyond saving me a few characters, I'll take them.
> You either like strongly typed object oriented langs or you don't.
Sorry, but this argument doesn't pass muster anymore, when there are abundant examples of strongly typed object oriented langs which don't exhibit these flaws. C# avoids getter and setter boilerplate with `get` and `set`. Kotlin and Swift, unlike Java, actually are strongly typed, because objects aren't implicitly nullable. And the many conveniences afforded by modern language features add up to improved productivity, provided the programmer using the language doesn't rigidly refuse to learn to use them.
It's about having non-nullables. It's about having value types. It's about controlling mutability. It's about the evils of (especially mutable) statics and life-before main. It's about having closures (at least recent Javas have them!)
None of this goes against strong typing or object orientation. Java just is... not so great. Of course, everything about this is hindsight: Java was also a revolution: generics (slightly flawed and bolted afterwards, but anyways), garbage collection, memory safety, JIT in an usable package.
Java has shown it's age and some people are aware of it.
I don't think that any of Java's warts are related to liking strongly-typed object oriented languages. I think the standard library is Java's biggest problem (read through the code that's been around since the 1.0 days, it's AMAZING in the way that a car crash is amazing). The community has done a good job building a better standard library (back when I did Java it was Guava, maybe that's not the state of the art anymore), and that helped a lot.
All in all, I am not a fan of OO anymore. I like the computer science principles behind it, but practical programs never use them. They always get Liskov Substitution backwards. People always use subclasses for cases like "give me the superclass, but with these conditions" which is the exact opposite of what you're "supposed" to do. Problem is making your subclass less restrictive is largely useless, what people really want is copy-paste without having to maintain two copies of the code. So there is some mismatch to what programmers and computer scientists call classes, and the result is that you get a mess. I'm not sure that's Java's fault or its largest problem, but it isn't helping.
The difference is more in a "batteries included" sense - Python values developer productivity and gives you what you need, similarly Haskell which is on the opposite end of a Dynamic vs Static spectrum, while Java was traditionally more oriented towards stability and predictability and partly attached to the old-school cool of potato programming.
I am sorry to disagree but if you think that the iterator or visitor pattern or the state pattern or abstract factories are only bound to Java than you have a very low skills/understanding of those patterns and you really don't understand their importance and/or applicability.
I think it's a cultural thing. Other languages don't name things that seem obvious, like iterators or filters. Consider something like filepath.Walk in go; you give it a function that gets called for every file and directory in the root of your choosing. They don't call it a visitor, though, they call it a "walk function". It's just as clear, it's just as much of a design pattern, but they don't make a big deal about it.
Meanwhile, I'm sure in Java that you have to implement some class that is the visitor, which means you have to create a new file, and probably install some VisitorUtils for making file visitors easier to implement, ... that is where it's gone off the rails. (But I am perfectly willing to believe that the exact same API exists somewhere in Java too, and most people browsing directory trees don't have 13 classes to print the names of the files in a directory. But at the same time, I feel like there are pressures pushing you in that direction. That's where the complaints come from.)
> Consider something like filepath.Walk in go; you give it a function that gets called for every file and directory in the root of your choosing. They don't call it a visitor, though, they call it a "walk function".
The ironic thing is that golang doesn't have an even better construct for this kind of pattern, namely pattern matching.
The majority of design patterns only exist because of the limitations of single dispatch. Java is far from the only single dispatch object system, but his criticism is still valid.
>When design patterns came along, Java folks talked about them as though they were a wonderful new invention.
Your history is wrong. Design patterns became popular with the release of the GoF book, which was written using C++ and was published in 1994 -- before Java was even released.
The remark about design patterns is not accurate. Design patterns did not originate with Java and aren't specific to that language. The classic book "Design Patterns: Elements of Reusable Object-Oriented Software", by the 'Gang of Four', was written in and about C++. It was published in 1994 and predates Java.
"Design Patterns", both as a book and a concept, is about the recurring patterns found in object-oriented software; it is about the problems that lead to those patterns, and the solutions to them. Although the book itself is rather dated, the patterns described in it remain relevant to most modern languages. Some of those patterns include: Builder, Singleton, Proxy, Decorator, Iterator, Visitor, Prototype (c.f. JavaScript prototypes), Observer, Chain of Responsibility, and more.
Many languages employ these patterns in the design of their standard library. E.g. the Iterator pattern shows up in a large number of language libraries (Python, Ruby, JavaScript, Swift, Rust).
If you search for "Design Patterns in X", for programming language X, you can find quite a lot of literature about how to employ them in any language. For example, see "Python 3 Patterns, Recipes and Idioms" [1], which describes how to employ many design patterns in Python, or "Design Patterns in Ruby" [2]. A lot of modern literature for many programming languages relies on these patterns either implicitly or explicitly.
Most of these patterns do not exist because of language weaknesses, although I would agree that this is true for some of them, such as the Visitor Pattern. Use of the Visitor Pattern is often better replaced with pattern matching in programming languages that have it. However, this isn't true for all of the patterns (e.g. Chain of Responsibility, Facade, Proxy); and even the Visitor Pattern remains relevant to many languages, especially dynamic languages that do not support pattern matching.
Many of these structural concepts were not new, in the sense that they did not originate with the book; but the GoF took the time to assign names, characterize them, and describe the trade-offs involved in using them.
"about the recurring patterns found in object-oriented software"
... in statically typed languages of that time (like C++ and to some extent Java). At least some patterns solve problems that are not (that kind of) a problem in, e.g., dynamically typed languages.
> This is similar to the situation with design patterns. When design patterns came along, Java folks talked about them as though they were a wonderful new invention.
They are/were a bit over-hyped and sometimes overused inside the Java community, that's true.
> But they only exist because Java is so clumsy that it needed crutches to do things that had been easy and natural in other languages for years;
Sure, some design patterns work around language deficiencies or are that commonly used to justify including them into the language. Java sadly did not do this enough in the past, that's right. But if you look through the list of design patters only a minority fall into this category.
> someone merely came along and gave the crutches names.
That's basically the whole point of design patterns. It's assigning a name to things already widely used to improve communication between developers and to provide learners a point to look up a concept.
You have to look at it from a time when C++ was the mainstream for large scale projects. Java is certainly a god-sent gift to software salaryman all over the world.
This is similar to the situation with design patterns. When design patterns came along, Java folks talked about them as though they were a wonderful new invention. But they only exist because Java is so clumsy that it needed crutches to do things that had been easy and natural in other languages for years; someone merely came along and gave the crutches names.