Hacker News new | past | comments | ask | show | jobs | submit login
Why I love everything you hate about Java (magicscalingsprinkles.wordpress.com)
59 points by nkallen on Feb 9, 2010 | hide | past | favorite | 65 comments



Um, I don't hate Factories, Dependency Injection and Decorators. I hate that Java makes it very cumbersome to use them, typically via xml configuration files (so that it can be dynamic) and adding complex syntax (generics, annotations). And I hate that I can not pass functions as parameters. As he says in the article: "function composition is a degenerate case of the Decorator pattern." (not sure why "degenerate") - so instead of just composing functions, in Java I have to write countless classes and interfaces. That's what I hate.


That's not Java. That's EJB. Or JPA. Or JBOSS. Or RMI. Or any other bad idea that's come out in the last 15 years.

Java's been out and big for 15 years -- every bad idea has had a big push for it in Java.

But the language? Something like a dozen keywords. With some really nice libraries like java.util.concurrent and the Collections API. Just because lousy designs have been written in Java doesn't mean it's a lousy language. It may be starting to get a little dated, but that's a different matter. It's basically becoming the C of the JVM at this point, with higher level languages like JRuby and Clojure to run in application space and Java as your platform space.


Not quite - to be able to use factories and dependency injection, you have to use interfaces. And not being able to use functions as parameters (or write them on the fly) is annoying in all kinds of software development, not just enterprise software.

Also, all the nice libraries have been updated to use the new language features like generics and annotations.


You don't have to use interfaces in order to do dependency injection, you just probably should, depending on the task. I wrote something last week which injected dependencies directly to a class. Interfaces are actually one of the best ideas in Java, IMO, if you're using them appropriately.

Generics and annotations are pretty useful. What's the matter with them? Have you spent time developing with them? They're certainly better than having to cast all over the place.

RE: functions as parameters.. yeah that's kinda annoying. You can always do new Runnable() { void run() { doSomeStuff; }} but that's ridiculous. So you have a point there. Aside from that though, the language is suitable for most tasks, and requiring a little extra syntax when you want to pass functions around isn't the end of the world.

Anyways, I'm sure I'm arguing against the trendy masses here, and I have a clojure book too, just trying to set up the opportunity to use it for something real.. but there's a reason why most "real" back-end stuff that has to be performant, stable and reliable winds up getting written in Java these days. That includes Hadoop, Lucene and half of the NoSQL key-value stores that you're excited about :)


A lot of the "real" performant/stable/reliable backends seem to be written in Scala and Erlang these days. People use whatever languages are production-ready state-of-the-art at the time they begin their systems. C++ was state-of-the-art when Google started in 1995; Java was state-of-the-art when Lucene/Hadoop started in the early 2000s; now people are building things in Scala and Erlang.

Anyway, be careful when using "big and accepted" projects to indicate ideal platforms. It takes 5-10 years for a project to become big and accepted. In the process, its technology platform has probably become obsolete, but continues on because of inertia. Your goal should be to shoot for what your role models would do now, with the benefit of 10 years of additional technical development, and not where they were 10 years ago.


You might want to double check your dates on Erlang. The first version was developed by Joe Armstrong in 1986.[1] It supports hot swapping thus code can be changed without stopping a system.[2] Erlang was originally a proprietary language within Ericsson, but was released as open source in 1998. http://en.wikipedia.org/wiki/Erlang_%28programming_language%...


I know how old Erlang is. My point is that the computing environment has changed in the meantime, such that it's now a viable alternative for servers.


Ok, my point was I recall some Erlang hype back in 90's. What specifically in your opinion made that 20+ year old language "production ready"?


The list I had in my original comment (which news.YC ate):

1. Software projects have increasingly moved from the desktop (where having a complicated runtime is a non-starter) to the server (where you can control everything).

2. Failover and hot swap capabilities are increasingly demanded for consumer apps.

3. It's now viable, from an efficiency standpoint, to have a language that copies data structures around instead of mutating them.

4. There's been a groundswell of interest in new programming languages, probably mostly because of #1. This has opened the door for old languages that previously lacked mainstream acceptance. (See also Python for an example.)


The computer industry focuses on three things, internal corporate systems, consumer applications / COTS, and embedded systems. Erlang was invented over 20 years ago because it was useful at the time for a wide range of applications. I happened to work on a business system written about that time that custom rolled a database, custom GUI, hot swap capabilities running on apple talk. It was recreated but that had more to do with OSX showing up than anything to be gained from the transition.

I would suggest that you have focused on consumer apps you may want to look up the rest of the industry because much of the new hotness is really old news. There seems to be a trend where people pull out an old idea, give it a new name and start hyping it, but new ideas are uncommon in computing.


I do hate Factories and DI. They are just buzzwords really. As someone mentioned in other comment - all those things he lists can be done in a dynamic language and some of them will be much easier.

Factories == constructors meet partial application and functions passing. They seem to be objects only because Java lacks nice support for bare functions...

DI / IoC == ... well ... good design. You're simply limiting the amount of state and coupling.

Decorators, proxies, etc. - duck-typing and metaclasses are a bit more flexible here.

And the first example is just passing a function to a threadpool - there's nothing very tricky about it either...

It seems to me that author never used a dynamic language properly :(


The problem dependency injection attempts to solve disappears when you have dynamic scoping. I've said this before - DI is probably the dumbest fad (no small feat) in object-oriented programming.


Could you remind me what dynamic scoping is? Thanks!


    f() { return x; }
    g() { x=1; return f(); }
f() returns x it inherits from g(). A spectacular way to shoot yourself in the foot.


Wrong on all counts. Dynamic scoping depends on having a construct to introduce new scopes (a fancy way of saying 'let statement').

Here's what the classic problem of "how do I test my code using a fake service?" would be solved with dynamic scoping:

(defvar service <default service>) ;; in Common Lisp you have to declare that certain variables will have dynamic scope, otherwise they would be lexical

(defun some-function () (use service))

(defun test () (let ((service <fake-test-service>)) (do-something (some-function))))

This is more than global variables, because now you can plug in any service you want in any runtime context you need without affecting other code.

If this looks a bit like AOP, it's because applying dynamic scope to functions is the actual underlying mechanism behind AOP: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.113...


That looks like no scoping at all? I think JavaScript is a bit like that? But most languages would prevent that kind of thing?


It's not no scoping - it's specifically scoped to where the call to f() was made, rather than where it was defined. It basically makes everything in scope in g() available to f() as a hidden parameter.

Pre-CL Lisps had this by default. defvar is, as far as I can tell (still learning!) how you get at it now.


Thanks for the info. At first sight it looks impossible to handle (how do you know what variables f expects), but maybe there is a technique to it I don't know yet.


What the hell has XML got to do with Java? You're confusing frameworks with languages. Most Java frameworks suck. Agreed.


No, it is not a coincidence that Java and XML go hand in hand. Heavy XML usage was introduced into common Java practice to compensate for the lack of dynamic features in Java. So dynamic features were introduced (badly) through the XML backdoor. Also often XML is used as a poor mans DSL.

It could have been something similar to XML (like JSON or YAML), but that is not the point. Also, the verbosity of XML aligns itself well with the Java mindset.


Utter BS. I've been doing Java programming for 10 years and have never touched XML thank god.

"the Java mindset" - more utter BS. There isn't a 'Java mindset' unless you're a bad programmer who is ruled by his language instead of using languages as tools.

The "mindset" you have depends on how good a coder you are. Which is the same regardless of which language you decide to use to solve a particular problem.


Fine, then you are the exception to the rule.

As for the mindset, it seems to me that at least SUN was very busy churning out cookbooks for a lot of things. Mostly Enterprise Java - if you managed to avoid that, more power to you. But I think most people tried to do things the "official" way.


Totally @ Enterprise. Horrible horrible stuff that, but really, there's some great technology in the JVM, and built in libs.

And the Java language is a pretty sane general purpose language. It's fairly easy to spot all the crap and avoid it.


Show me a java app with no xml, and I'll show you a hello world program.


The Mibbit backend, Mibbit webserver, and everything I've written in the last 10 years, for example.


Quoth the author:

In a very dynamic language like Ruby, open classes and method aliasing (e.g., alias_method_chain) mitigate this problem, but they don’t solve it. If you manipulate a class to add logging, all instances of that class will have logging; you can’t take a surgical approach and say “just objects instantiated in this context”.

This is not true in Ruby!

  myinstance = MyClass.new
  def myinstance.special_method()
    puts "only on these instances!"
  end
  myinstance.special_method()
The author's point about avoiding unnecessary binding is well taken, but he should refrain from declaiming on languages he doesn't know that well.


Since you don't control the code that manufactures instances of MyClass.new, you don't have an opportunity to manipulate the metaclass/singleton class of instances of MyClass. So this technique doesn't work.


Dunno about Ruby, but in Perl and Common Lisp you just rebless the instance into a subclass that you do control.

http://www.lispworks.com/documentation/HyperSpec/Body/f_chg_...

http://transfixedbutnotdead.com/2009/07/07/moose-singleton-m...


Since you don't control the code that manufactures instances of MyClass.new

At least I didn't until I monkeypatched MyClass#initialize at runtime. cackles

Seriously, don't do that. But it is fun to contemplate.


The basic idea is adding methods to single instances. It _does_ work. What you're talking about is sugar. You could easily create a factory which manipulates instances as they are created.


Yes but you still need to inject the Factory. My point is not that Java is better than Ruby but that this style, which is commonly associated with Java, is better in any language.


Parameterizing behavior is usually a good thing in any language, yes...

The part that Python/Ruby got right is that their culture says don't parameterize until you have to. While Java culture tends to build in generality for the sake of generality, whether or not the actual system needs it. Many systems don't, but if you use typical Java libraries, you need to pay the costs for it regardless.

There was a red-flag in one of your comments above: "since you don't control the code, this technique doesn't work". If you don't control the code enough to make necessary modifications, you have a cultural problem, not a technical problem. Fix the culture, not the code. You should be building the simplest system that possibly works, not hacking around code ownership restrictions.

Parameterization is useful when you have one piece of code that you know needs to work in multiple contexts, not when you're trying to make sure a piece of code will work in all possible contexts a priori.


Isn't that the exact opposite of what the original quote was decrying?

And you can always open the eigenclass of any object in ruby for the ultimate surgical operation (THIS INSTANCE, RIGHT HERE!). It's pretty much the ultima ratio regum of special casing in any object oriented language.


One should probably not assume others "don't know [ruby] that well" when they don't know the person.


This article has nothing to do with Java. You can write modular code in any decent language. In fact, I would argue that the author's approach would work better in a more dynamic language.


Java especially in its enterprisey form is reasonably good at covering the maximum number of possible use cases, but it's bad at making the common cases simple. Yes, I should be able to write the one-line Future without boilerplate to get reasonable default behavior, and then add custom configuration later if I actually need it. That both reduces the amount of code and makes nonstandard usage more prominent.


Yes, this is exactly what Java doesn't get. Reading a file with only the standard libraries is 5 lines of code. Hello world is 6.

The main problem is that this encourages cargo-culting. Once someone has something working, they will just cut-n-paste-n-modify it in similar situations. Better to cargo-cult 10 lines of code than to write 5 of your own, right? The moving parts won't cut off your fingers as long as your fingers don't get too close.

(Normally I would rant about how "proper abstraction is not possible with Java", but it actually is. It just seems that the majority of libraries and programmers would rather cargo-cult than to design proper abstractions. And no, "proper abstraction" is not always "cut-n-paste from GoF".)


This is why I love .Net. Want a quick and dirty file read?

string fileContent = System.IO.File.ReadAllText(); Want to read it into lines? string[] fileLines = System.IO.File.ReadAllLines();

Want fine tuned byte by byte access? var fs = new System.IO.FileStream(path);

They certainly looked long and hard at Java before writing .net, and covered many of the sore points carefully.


Not all of those methods were in .Net 1.0. I worked through the painful years of .Net 0.9 to .Net 2.0 and it wasn't always roses. The core libraries were really terrible and sparse in the early versions.

Microsoft was a lot more aggressive in how they extended the core libraries post-2.0. There was an amazing amount of baggage in the language by the time 2.0 came out.

FWIW, you can do the first two items you mention using the Apache Commons IOUtils class in Java. The latter is part of Java:

http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileInputStr...


I think you've missed specifying the filename, but otherwise cool.

Here's Ruby for comparison:

  file_content = IO.read(filename)

  file_lines = IO.read(filename).split("\n")


    file_lines = File.readlines(filename)
Not that it matters hugely :-)


True enough. Why do I have to write this:

  SingleFilenameFilter myFilenameFilter = new SingleFilenameFilter()
  List myList = new File("/tmp").listFiles(myFilenameFilter)
Why should I have to create a class that implements FilenameFilter, just to filter on some file names? Especially in a one-off instance, why should I have to create a class that I will only ever use once?

Why isn't there a default implementation? Something like:

  public class RegexFilenameFilter implements FilenameFilter {
    private String regex;
    public RegexFilenameFilter(String re) {
      this.regex = re;
    }
    public accept(File dir, String name) {
     // matching here
    }
  }
Then I could just write:

  List relevantFiles = new File("/tmp").listFiles(new RegexFilenameFilter("^tmp\\.[a-z0-9]+$"))
Why should I have to create this class for every project (or create my own little 'utils' library)? It's easily one of the simplest usages of a FilenameFilter.


anonymous inner classes are your friend (poor mans closure):

  File[] textFiles = directory.listFiles(new FileFilter() {
    public boolean accept(File file) {
      return file.isFile() && file.canRead() && file.getName().endsWith(".txt");
    }
  });


File enumeration has sucked for a long time in Java. java.nio.file should hopefully fix this in 1.7, but I haven't had a chance to play with it.


That's Scala, not Java.

And this doesn't even begin to cover everything I dislike about Java.


Fine sir, I believe you have missed the point.


Disagree. There's 'boilerplate' introduced by the framework/pattern, which may be necessitated when you throw out default assumptions to scale (which I, wooster, the OP, and yourself probably all agree are not so cumbersome after all). Then there's boilerplate introduced by the language. That's where wooster diverges, and I agree.


I was a little confused by the fact that the post lists Scala as one of the "hipster" languages in the opening paragraph and then starts using it as an example of the java way? Just a slip?


"I was a little confused by the fact that the post lists scala as one of the "hipster" languages in the opening paragraph and then starts using it as an example of the java way? "

Well once you define the "java way" to be "build highly configurable code by providing a way to combine small granular pieces of code into larger chunks of functionality" (which is just Programming 101 , See SICP for e.g) it probably still makes sense to write the "java way" programs in Scala ;-)

Makes sense to me! :-P

As nostrademons says elsewhere on this thread ,"Parameterizing behavior is usually a good thing in any language, yes..."


Writing Java apologetics with using Scala sounded like a great idea at first...


I think folks who complain about java must make sure that they are using a very powerful ide first (example: eclipse or intellij idea) If, having used and mastered a good ide and all it's features, you still don't like it, it's a fair point.

Eclipse can make so many things in java drastically fast and easy. And I think that's where java shines. I'm yet to see as powerful an IDE for any other language. (Although I admit, I do not program for msft platforms and hence, never used msft ides)


I personally think the necessity of using an IDE merits complaint itself.

Yes, Eclipse and IntelliJ do some pretty amazing things. I wish I had a toolset that nice for C/Python/whatever. By contrast, without the toolset in Java, you're sunk.

Then again, I wouldn't imagine or expect some types of development to be achievable without a rich toolset (FPGA work comes to mind). I just like to think of Java as a more general purpose language.


This. I work on Java in Eclipse every day; it actually won me over from years of Emacs. But heavy-duty code navigation tools just manage the pain of bad abstraction, they're no substitute for a DRY language in which the work remains clear and concise enough that you can just sit down and read it.


I think that's a misconception.

FWIW I hate IDE's, and the code produced by people using them gives Java a bad name for its verbosity and idiocy.

>> without the toolset in Java, you're sunk.

No, you're not. Try it. Use an editor, write some code. It's not rocket science.


Here's a Java IDE question: why doesn't Eclipse have a REPL in the default install? That's the one thing I use most in other languages. It's nice to be able to right click to rename things (although a perl oneliner works just as well in practice), but it's nicer to be able to know that your mini-algorithm works before you design a class around it. Why is the focus on "rename" and "extract method" instead of "write code that actually works"?


IntelliJ does actually have a "REPL" built in. Although lately, I've simply been using the Scala REPL and/or Clojure+Slime to verify whether a Java library works the way I expect it to.

(I also use "perl -d" as a REPL for Perl, which it does quite well)


Devel::REPL is a better Perl REPL.


Instead of writing throw-away code in your REPL you can write a unit test around your function and you get a permanent unit test as a bonus.


I would generally say the same thing, but most of the experimentation I do in the REPL is "is this crazy thing possible", not "is f(4) = 3.23432198"?

And sometimes it's just not worth testing, because it's something like ``hmm, I wonder if 'intercalate ", " ["foo", "bar", "baz"]' returns 'foo, bar, baz'; ahh yes, it does.'' The only reason to test that is because you want your test suite to run slower.


As jrockway says in this thread. The REPL is most useful for exploratory coding, which a unit test is not good for.

Why write a unit test for a feature of the language? The Language designer probably already did that and all you need to do is write unit tests for your own code. I do a fair amount of clojure code now and find it highly useful for just running java code to see how it behaves. Which then helps me figure out how to code my actual code, which then will pass the test that I've already written.


I use the Jython (or Scala) REPL to explore Java.


I regularly use Eclipse for work and I find it to be horribly lacking. For one, I wouldn't even dream of running it on my eee pc (actually, I tried and it was so sloooow I quickly uninstalled it again - and yes, I do use my eee pc for development a lot, especially when I'm not at home). Secondly, I often write code over a remote connection. I can use my IDE of choice over SSH without loss of features - can you do this with Eclipse?

I use vim, which when coupled with the right configuration and plugins can do most things your fancy IDE's can do, plus run in a temrinal over ssh.

For the record, in work I program a lot of Java and some C++, outside this, I work a lot in Clojure and Python, amongst other things (assembly, Boo, Yeti and recently F#).


Abstraction is essential in building complex systems but it comes at a price. Every layer increases the overall complexity of the code base and increases the cost of maintaining and extending it.

Ideally languages and tools would allow you to begin with concrete & explicit code and gradually introduce increasing layers of abstraction as necessary. The problem I have with most Java frameworks is that they assume you're going to need a lot more abstraction than you likely need, so just getting an idea off the ground takes a lot of extra work.

It's really just worse is better all over again. The reason things like Ruby and Rails are so popular is that they let you toss together something that "works" so much faster. It's a lot easier to rewrite a good first-mover app than it is to try to hoist all that Java baggage into a new problem space you don't understand.

Come to think of it, isn't this exactly what Twitter did? If Twitter had been written as the article suggests would it have ever taken off?


It's interesting to consider that Ruby blocks are essentially function composition with an anonymous function.


"If you’re one of those hipster programmers who loves Clojure, Ruby, Scala, Erlang, or whatever, you probably deeply loathe Java and all of its giant configuration files and bloated APIs of AbstractFactoryFactoryInterfaces."

Can we just get a moratorium on usage of "hipster" now? Everybody's using it wrong.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: