In order to adopt a language that isn't in the top 6 or so, you've got to have good reasons for it. And depending on the problems you're trying to solve, sometimes it's worth it, sometimes it isn't. For a startup, with a small but capable team that's trying to outrun its competition to market, then using the most productive tools possible is a good choice. In other contexts though, like in a big company where long-term maintainability might be more important than speed to market, well, Java might be the better choice, and not because of the language per se (I think Java code is often unreadable), but more because the cost of maintainability is low due to its popularity and its slow pace of change.
I'm not saying Java is good looking by any means, but I've never found it to be heard to read in the "I can't completely grasp exactly what everything is supposed to do" kind of way. I often find Python and JS to have enough shortcuts, callbacks, and anonymous functions to find other people's code much harder to grok than other people's Java.
Not bad_user, obviously, but excessive verbosity can be antithetical to readability. (As can excessive conciseness.)
It can make the overall structure and meaning of the code hard to discern, even though can clearly tell where what gets assigned to what variables, etc.
Were popularized in the PC, Amiga and NeXT eco-systems, thanks to Turbo Pascal, Delphi, VB, AMOS, GFA, Objective-C, Eiffel, Oberon, ...
Java wasn't even a thing in those days.
Which is very hard in languages that are a pile of hieroglyphics.
This is very important in teams of 50+ developers, scattered around countries with high attrition rate, having various skill levels.
Also generating boilerplate is just 1% of what an IDE is capable of.
Personally I don't understand why some programmers insist on communicating only in english words. First of all because english words suffer from the problems of natural language, which is that natural language is imprecise, with the words having multiple meanings depending on surrounding context. For example when adding two numbers, should you use "add" or should you use "plus"? As it may be, "add" is actually incorrect according to its precise English definition, because the operation doesn't necessarily lead to increasing the size or amount. Yet this does not stop people from using it. Fortunately classes like BigDecimal are using "plus", yet I don't get why in the world would anybody think that "x.plus(y.multiply(z))" is more readable than "x + y * z".
You might thing picking on BigDecimal is a cheap shot. Well, how about the well known "ListUtils.union(a, b)" and why would that be better than "a ++ b". Speaking of "ListUtils.union", joining 2 lists is not a union, as union is in the context of sets or maps. Joining 2 lists is a concatenation and that matters, because concatenation is not commutative, whereas a union (of 2 sets or maps) is commutative. Basically the Apache Commons folks have got the naming wrong and if such mistakes happen in libraries that are so public, guess what happens inside corporate projects.
But much more problematic in languages like Java is not the wording as much as the way the logic is often expressed. In a functional programming language you usually get pure expressions that operate on immutable data-structures, pure as in true mathematical functions (for the same input you always get the same output). Such functions are much easier to reason about and much easier to test, because the output does not depend on some object's history (or in other words, an object's identity).
The worst and most unreadable code I've ever seen was written in Java, a language in which people often pass around mutable data-structures, like arrays, modifying them on the spot, for no good reason other than not knowing any better, in a dance of mutation that can only be considered an abomination of nature, with code so obtuse that it would make grown men cry. It's not uncommon for pieces of code to be commented with "here be dragons" with people no longer understanding what it does. Couple that with "enterprise design patterns" based primarily on IoC containers and best practices spawned from hell, with deep layers of inheritance that don't make sense and with chronic multi-threading issues and you've got a recipe for disaster. And you don't even have to search for proof for too long. Take any reasonably sized open-source project and you'll see this fact in all its glory. The last Java open-source project I interacted with is SpyMemcached and is a fine example of a Java project that works, that does its job well, that is reasonably well maintained, but that has internals that expose all the problems that I just mentioned. And sometimes I wonder why anything written in Java works at all, my guess being simply that people hit that code with the hammer until it quacks, in a process that resembles more natural evolution and mutation rather than engineering.
Therefore I'm personally dumbfounded by claims of readability. And in regards to the capabilities of an IDE, I do use IntelliJ IDEA, but I am wondering what the other 99% of its capabilities are, preferably that help with readability. Syntax highlighting?
You see, Go is not popular enough to have users that don't like it. The people that end up trying out Go simply move to something else if they don't like it. Go's community is also strongly opinionated and has rejected any dialog for meaningful improvements. In other words the Go community is filtering out people that want something different. Whether this is good or bad, you be the judge, but if there's one thing that's definitely bad is the echo chamber. Case in point you're under the impression that Go is tolerable, even though many of us consider it to be worse than Java.
On the contrary, I think the recent additions from Java 8 are a step in the right direction.
Except almost all the tools work with the pre-java-8 way of doing things (and often even pre-java-7). Thus, you end up with layers of different idioms, all of which are not quite compatible with each other.
For Java code, it's often easier to predict the performance characteristics of a part of the code. But you shouldn't trust your judgements anyway - instead use a profiler - so this is not really a reason.
Speaking of performance: I once talked to someone working for Elasticsearch, and they told me that they cannot use clojure for most of their code because of their performance requirements. Clojure collections are almost as fast as Java collections in many scenarios, but that is apparently not fast enough for them. This is probably not a valid reason for most projects, though.
There is a huge number of Java developers out there, and Java is easier to learn for someone coming from C++ or C# or JavaScript than clojure (at least I am pretty sure about that). So hiring cheap developers might be easier if you use Java. You might count that one as a political statement.
There are some great tools for Java out there: IntelliJ Idea, Yourkit Profiler, JRebel, ... Cursive Clojure is great, but IntelliJ for Java has even more integrated workflows.
There are very mature open source libraries / frameworks / tools for Java: JUnit, Spring, Guice, Dropwizard, Gradle, Elasticsearch, ... You can have commercial support for many of them. Most of the libraries I use with clojure are version 0.3 or something like that with no possibility for commercial support (but they work fine anyway).
Static typing: A lot of people complain about it, but it can really help you to create maintainable code. See for example http://talks.samirtalwar.com/use-your-type-system.html
Clojure has a weakly integrated type system (an inherent disadvantage of optional type systems). At the simplest level this makes silly errors much easier and means you have to write more tests to maintain the same defect rate.
The lack of types mean you require extensive use of macros for advanced functionality. IME macros have major maintainability issues in a multi-person codebase.
Both these things are major disadvantages for automatic comprehensibility of code. Autocompletion can be more-or-less usable but will never be as good as in Java or Scala. Automated refactoring is inherently unsafe in the presence of macros (Scala's fancier typed constructs (typeclasses, for/yield with custom types) mean you need macros much less often; Java tends to force you to expand these things out by hand (or else use annotations which act as de facto macros), which has its own maintainability issues but does at least mean automated refactoring will work correctly).
Some of the language culture pushes people towards less principled abstractions. From this side of the fence that looks like anti-intellectualism; no doubt from their side it's pretension on typed programmers' part. But either way I think they're setting themselves up for long-term maintainability issues (e.g. the semantics of clojure transducers in the presence of errors are infuriatingly not-quite-right, which will either remain a painful gotcha forever, or necessitate a painful migration in the future).
As a minority language Clojure may not be as well supported in the surrounding ecosystem - partly things like IDEs but also code coverage tools, profilers, monitoring.... Remember the JVM ecosystem is wider than just the languages themselves.
There are good things about Clojure, but it's by no means clear-cut.
In terms IDEs, Cursive for IntelliJ is great, and it gives almost Java-like capabilities (with limitations inherent to more dynamic languages, of course).
I have heard this a bunch, and I believe it, but I've never personally had an opportunity to use a language that supports macros for a multi-person codebase. I'd be interested to learn what are some of the pitfalls you've seen, if you don't mind sharing.
Lisp got a lot of things right but I'm not sure dynamic typing and s-expression syntax were among them.
Could you elaborate?
They're conservative about adding features, so they've managed to keep the language pretty small, and the core concepts of "what is good Java code" have been mostly the same for like 15 years, even through major releases like 1.5.
Java developers usually consider features which the language doesn't have to be bad or make code unreadable, that is until those same features are added to Java, when suddenly they are a source of pride and a sign of Java being "modern". In reality Java's feature freeze until Java 8 was largely driven by Sun's financial woes.
Map<String, Customer> customerMap = createCustomerMap();
where in other less verbose languages you find val customerMap = createCustomerMap();
Always seeing the definition of a value in the current function context is actually very nice for maintainability, but does give Java the reputation of being overly verbose.Stuff like getters/setters on all values in a class are awful though and are both a code smell and unnecessary. They break the OO principles and should not be there in the first place. It's good that Java makes those 6 screens worth of getters and setters awful - they are awful. For objects which are used solely for transfering state, check out Google's AutoValue ( https://github.com/google/auto/tree/master/value )
However if you do see a class that is 6 screens of just getters and setters then you know exactly which class you need to fix.
There are also many less favourable cases where you see things like:
Customer customer = new Customer();
String str = "I'm a String"
SomethingOrOtherFactory factory = new SomethingOrOtherFactory();
which isn't any clearer than: val customer = new Customer();
val str = "I'm a String"
val factory = new SomethingOrOtherFactory()
Having a dozen getter/setters is generally a code smell, but having a dozen getters isn't. Animal customer = new Cat();
It makes it clear how the new object will be used. It does come up quite frequently, especially with Collection and List. Nobody was saying it is not verbose and can lead to boilerplate. The assertion is that when viewing a large code base, you are always sure of the type of value you are dealing with on a local level. If all of your objects are well defined, you don't even need to look at how the class is created (a billion getters and setters? irrelevant to the local function) as you only need to deal with the local function. val customer = new Cat(): Animal
I can see arguments against inter-method type inference, but I think there's no real argument against type inference within a single method (requiring explicit types on method boundaries). If you have a billion-line method you have bigger problems.Comparing to the original implementation of properties in C# (not how they look now), the Java way wasn't that different.
They just copied what was common C++ and Smalltalk practice back then, whereas Anders obviously had his Delphi experience.
Not that they shouldn't eventually improve it.
The main choice is then either Clojure or Scala. This comes down to dynamic versus static type checking. With a dynamic language, one is significantly less sure if a program is correct. In large programs that use dynamic languages, many tests must be created that are essentially doing the work that a type checker in a static language would do automatically.
In a static language, entire classes of bugs are eliminated. Scala provides the best support for strong, static typing on the JVM.
I'd argue that Ceylon and Kotlin offer most of Scala's advantages with none of its baggage and bloat. And drama.
Kotlin and Ceylon both have very good IDE plug-ins that already work much better than Scala, despite Typesafe pouring a lot of money into their own plug-in.
There are however areas where I'd use clojure every time. Luckily the JVM is really good for doing mixed language work on, so you don't have to work entirely in one language.
It's not some kind of war or personal affront to you - some people simply prefer the Algol style to the Lisp one. This even includes people who are very fluent in the Lisp style.
Clojure and Scala exist because of the JVM, not because of Java. Java 8 will or won't be an interesting language independently of the JVM, oddly enough.
So you need to be comfortable with it, as there will always be cases you need to jump out of the alternative language into Java.
Also, most companies will only hire to work with Java on the JVM, because they don't want to be hostage of the cool language some of the dev team guys/contractors decided to use instead.
It is very rare to see traditional Java shops to use alternative languages.
Usually the ones using them are either startups or companies like Facebook and Twitter, which aren't the typical business culture.
I would suggest just keep using Clojure and when it makes sense, mix in Java when you want or need to.
If you want a dynamic language on the Java platform; then why not use JavaScript?
There is an easy-to-use engine in Java 8 and JavaScript is widespread and somehow familiar to Java-developers.
If you are stuck with a group of developers who have no interest in learning anything new and wish to use Java until they retire then Java is the only choice. Other than no new learning required I can't think of many reasons to pick Java as a first choice. Java tends to be popular because it's the oldest, most familiar default for the JVM rather than any particular strength.
Put another way: if you're on a dev team where nobody knows Clojure/Scala, and everyone knows Java, (and, optionally, the codebase is already in Java), and your team doesn't have time for everyone to learn an entirely new language (including cleaning up the beginner's mistakes that come with that process), then Java's the better choice.
Don't get me wrong, Java grinds my gears sometimes, and I frequently wish I could use Kotlin or F# or some kind of typed Ruby in my Java-shop workplace, and I enjoy picking up the cool ideas from languages like Idris (dependent types are really cool!) in my free time. But I'm sympathetic to my coworkers in the environment where I work: 4 devs in the whole company, startup pressure to get to a break-even point so we don't go under, and a backlog longer than all of us could finish this year even if we froze it now.
Sometimes circumstances require what looks like a short-term decision in order to ensure that there's a long term to worry about later.
I bet you will find thousands of C++ devs that don't even know that there are newer versions of C++98.
C devs that still use K&R as their guide with C89 code style.
And so on.
We are the few select ones that care to improve our skills.
EDIT: Typos.
Actually had a guy tell me (in 2006!) that the main problem with C++ was that there was no standard. :|