The Artima Developer Community
Sponsored Link

Weblogs Forum
Have Generics Killed Java?

62 replies on 5 pages. Most recent reply: Aug 8, 2010 6:43 PM by Eric Armstrong

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 62 replies on 5 pages [ 1 2 3 4 5 | » ]
Eric Armstrong

Posts: 207
Nickname: cooltools
Registered: Apr, 2003

Have Generics Killed Java? (View in Weblogs)
Posted: Jul 21, 2010 9:49 PM
Reply to this message Reply
Summary
In which I argue that (a) Generics have done egregious harm to both the elegance and readability of the Java language and, (b) they prove by example that static type checking is a linguistic dead-end. Are you persuaded? Do you agree? Read on...
Advertisement

I confess. I'm a Ruby hacker, at heart. Ruby's Perl-isms can't go away fast enough to suit me, but what's left after removing them is a thing of utter beauty and precision. Not to mention power.

But long before I became enamored of Ruby, I was a Java hacker. I've recently come back to Java, and was rather surprised by the strength of my reaction--mostly as a result of generics.

I admit to being mildly annoyed by all the semicolons, braces, and parentheses that the compiler forces me to supply. It makes me feel like a slave, working for a not-very-bright master. I mean, the compiler could be smart enough to know what's missing. It just isn't. It prefers to give me an error, instead.

And of course, I miss Ruby's functional coding style, closures, and the flexibility provided by duck typing, open classes, and dynamic hooks--things that give the language it's expressiveness and power.

But what's really killing me is generics.

Generics Considered Harmful

Don't get me wrong. I continue to admire Generics as a tour-de-force of implementation genius. Getting them to work in a backwards-compatible way was nothing less than brilliant.

At the same time, I decry their very existence. They are the ultimate condemnation of static type checking--because their very addition has virtually destroyed both the readability and elegance of the language.

To make my case, let's start with a quasi-mathematical definition of "elegance". In my view, elegance is a function of power and simplicity. Power is measured by how much you can do. Simplicity is the inverse of the number of characters it takes to achieve the result. So more power with fewer characters equals "elegance", in my book.

Of course, by that measure APL is a very elegant language! And it is. But it's famously unreadable. Somewhere, there is a quote from a very smart guy who spent 4 days figuring out what a 4 line program did-- a program that he himself had written a few months earlier. That has to set some kind of standard for unreadability.

So "elegance" has to be tempered with the equally important notion of "readability". Readability is also a function of the number of characters on the page, but it has a bell curve: Too few characters, and the code is indecipherable. But with too many characters, the code is effectively obfuscated. It becomes impossible to see the forest for the trees. At that point, the code is obscure.

Between "indecipherable" and "obscure", lies the domain of "readable". That realm is the sweet spot. It's one that Java has been pretty darn good about occupying.

Until generics.

With the advent of generics, elegance has obviously been impaired (more characters, same power). At the same time, I claim that generics have pushed the language into the realm of obscurity.

That is an entirely subjective assessment, I admit. People have different levels of tolerance for that kind of thing. I mean, there will always be folks who prefer

SALES MINUS COSTS DIVIDED BY YEAR
to
(sales - costs) / years

To each their own. But there is no doubt that with the advent of generics, Java code is less readable (and therefore more obscure) than it once was.

The result is a two-fold loss--a loss of both elegance and readability--that to my mind is nothing short of a travesty. It represents the virtual destruction of a good language at the hands of something that undoubtedly seemed like a good idea at the time.

When I look at code I've written using generics, and mentally strip out the generic portions, I can't say that the code is any better or worse than it was before. Generics just don't seem to be adding any real value.

Of course, generics create greater "type safety" at compile time. And that, I think, is the ultimate condemnation of static type checking: They show how much work you really have to do to get it. And what is your return for that investment? Not much, really. You get a small percentage increase in the number of real errors found at compile-time.

But to get that marginal increase, you spend more time adding required syntax, watching the compiler find silly omissions and typos, and making changes solely to satisfy the compiler. In return, you get code that is less readable.

If all that work produced code that was truly bulletproof, it could easily be worth the investment of effort. But it doesn't. You still have to do an enormous amount of testing to get any real quality. And by the time you've done all that testing, static type checking winds up being superfluous.

Sure, in some super-critical applications like the Mars mission or a stock exchange, the tradeoff may be acceptable. Even a tiny improvement in quality is worth a significant investment, because the cost of a defect is so high. But for day to day programming, the tradeoff just isn't worth it, in my book.

An Example is Worth 1,000 Words

Enough with theory. Let's look at an example.

I'm a big fan of the "foreach" concept. A phrase like this one clearly communicates the code's intent:

foreach item in List {
  ...
}

In Ruby, the equivalent mechanism is even cooler, once you get used it:

List.each { |item|
  ...
}

It's cooler, because "each" is a method that can be applied to any collection.

Sidebar: Going Crazy with Functional Expressions Since every block is an expression, and every expression returns a value, if you want to go nuts, you could even apply a dot-operator to the block:

List.each { |i| ... }.doSomething()

Crazy, huh? You wouldn't do that often, but there are times...

Want to iterate over a Map, processing each key/value pair in turn? Here's the syntax:

Map.each { |key, value|
  ...
}

And now here's the equivalent expression in Java, with generics:

for(Map.Entry<K, V> e : map.entrySet()) {
  ...
}

Sorry, but that's an expression that only a mother could love. Without generics, it's not too bad. With generics and the other syntactic elements to required to make it legal, the statement verges on unreadable.

In general, the trick to reading such loops is to translate the litte colon to "in" and, once you see the colon, to mentally convert the "for" in the expression to "for each". So with a simple, "unsafe" loop, when you see this:

for (item : List) {
}

You read:

for (each) item (in) List ...

That's not bad. But lets look at the Map example again. This time, adding the statements needed to get values to work on.

For comparison, in Ruby we would write:

map = getInfo();
map.each { |key, value|
  ...
}

But in generified Java, we need this:

map = getInfo();
for(Map.Entry<K, V> e : map.entrySet()) {
    String key = e.getKey();
    Object item = e.getValue();
    ...
}

With the generic declarations added in, plus the need to invoke a method on the map, and all of the parentheses and angle brackets that are required, the colon is hard to see. It is effectively lost amid the noise. You have to peer at the expression carefully to discern its intent--especially when you come across it in the context of a larger program.

In fact, it was after copying that very expression from the web that I became disenchanted with generified Java. For me, Java's huge advantage from day one has always been it's readability. (The Web interface library didn't hurt, either. But this is about the language.)

With Java, there was only one way to do anything. There were no #ifdefs or home-grown macros, so no matter whose code you read, you always knew exactly what it was doing. So it easy to read. In consequence it was (relatively) easy to learn.

Ruby is more like Perl, in that regard. There are so many ways to do things that, to read anyone else's code fluently, you first have to master the particular set of idioms they employed.

So score one for Java in the realm of readability. Until generics. A subtle syntax element like ":" can't stand up against the deadly incursion of ubiquitous type-declaration syntax. To even begin to compete, it would have to employ COBOL-like syntax--in all caps, at that:

for(Map.Entry<K, V> e IN map.entrySet()) {
   ...
}

That, at least, would be a little closer to readable. But of course it would be even less elegant.

But wait, there's more

Things get even a little worse if we want to process the map in an ordered way, and we assume that the original map isn't necessarily ordered.

Here's the Ruby expression:

map = getInfo();
map.sort.each { |key, value|
  ...
}

Pretty cool, huh? You merely invoke an intermediate sort() method to get the result you want. It's even more readable because parentheses on method invocations are only required when they are absolutely necessary.

Here is the equivalent operation in generified Java:

Map<String,Object> map = getInfo();
Map<String,Object> sortedMap = new TreeMap<String,Object>(map);
for(Map.Entry<K, V> e : sortedMap.entrySet()) {
    String key = e.getKey();
Object value = e.getValue();
...
}

Readable? I think not. It takes more than a quick glance to tell what that code is doing.

Anticipating the Counter Arguments

Generics force the programmer to do at something the computer is entirely capable of doing at run time (type verification). And if we have learned anything at all about computers, it's to let them do what they're good at!

When you add generics to the requirements for parentheses and semicolons (other things the compiler could easily supply), the scales tip quickly away from readability and elegance.

Of course, runtime type checking makes your code more "unsafe", so you have to do more testing. But when you're not spending all of your time adding syntactic "sugar" to make the compiler swallow your code, you have a lot more time to make sure your code is doing the right thing.

Note: That's right. I just called generics "syntactic sugar". In this case, the sugar makes the code more palatable to the compiler, not the coder. You have to pour it on, but you don't get to enjoy it.

Equally horrendous, to some, is the fact that runtime type checking slows things down. But computers are getting faster, the number of programs that needs to be written is exploding, and the number of programmers available to write them is shrinking in comparison.

Given that state of affairs, it's hard to see the point of sacrificing readability and elegance, all in the same breath.


Eric Pederson

Posts: 12
Nickname: 71552
Registered: Apr, 2010

Re: Have Generics Killed Java? Posted: Jul 21, 2010 11:17 PM
Reply to this message Reply
There are productivity as well as quality benefits to catching errors at compile time rather than runtime. But I agree with you about the readability issue with Java, especially when you throw generics into the mix.

You should check out Scala. It is type safe but the compiler is smart enough to infer type and other semantic information without requiring the extra syntax. Readability was a primary goal in the design. And runs on the JVM with full interoperability with Java code, so you can leverage the huge Java ecosystem.

Being a fan of both Ruby and Groovy I have been very pleased with Scala.

Bill Venners

Posts: 2242
Nickname: bv
Registered: Jan, 2002

Re: Have Generics Killed Java? Posted: Jul 21, 2010 11:29 PM
Reply to this message Reply
Great post Eric. I've got two comments, though.

One is that I think you are blaming static typing for something that's more a fault of Java than static typing. It is like concluding that because Perl code is obscure and Perl is a dynamically typed language, that therefore dynamic typing leads to obscure code.

My other comment is that where I find the benefit of type errors being found by the compiler really pays off is when refactoring mature programs. It isn't so much when I'm writing something for the first time, because I'll be focused on the new functionality then and making sure it works, but when I make changes to it later that affects many things and I may not be aware of all that I'm affecting. Yes tests are helpful there too, but in practice I find static type checking very useful at that time.

Michael Galpin

Posts: 2
Nickname: mgalpin
Registered: Jul, 2008

Re: Have Generics Killed Java? Posted: Jul 22, 2010 12:50 AM
Reply to this message Reply
-1, Flamebait

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Have Generics Killed Java? Posted: Jul 22, 2010 12:57 AM
Reply to this message Reply
Great post. Java is making some progress in this area with inference of generics, e.g. the following is scheduled for JDK7:

List<String> strings = new ArrayList<>();


Which isn't much but it is something. Also they are adding lambdas and default methods for interfaces, so you will be able to write:

strings.sort().each( { s -> out.println( s ); } );

Cedric Beust

Posts: 140
Nickname: cbeust
Registered: Feb, 2004

Re: Have Generics Killed Java? Posted: Jul 22, 2010 12:57 AM
Reply to this message Reply
Eric,

I agree with some of your points and disagree with others.

Yes, the generic syntax makes Java harder to read.

Yes, the Ruby closure syntax is great.

Where I think that Ruby falls flat:

Duck typing: evil and dangerous, avoid it [1]

Dynamic typing: great for quick prototypes, terrible for large software shared by large teams. Dynamically typed code is harder to maintain and most of the automatic refactorings are impossible to achieve [2]

At the end of the day, I still find myself extremely productive in Java because of the exceptional tooling that accompanies it, some of which simply cannot be done in dynamically typed languages.

[1] http://beust.com/weblog/2008/02/11/structural-typing-vs-duck-typing/

[2] http://beust.com/weblog/2006/10/01/dynamic-language-refactoring-ide-pick-one/

Scott Kilpatrick

Posts: 1
Nickname: skilpat
Registered: Jul, 2010

Re: Have Generics Killed Java? Posted: Jul 22, 2010 2:19 AM
Reply to this message Reply
I'm sorry, but I found this entire article to be grossly misleading and ill-informed. The only valid point you've made is that -- as you mention with semicolons, braces, and parentheses -- requiring the programmer to manually apply type arguments is usually a pointless exercise in pedantry. As other commenters have pointed out, both Scala and an upcoming version of Java relax this requirement with some pretty basic local type inference.

You argue that the introduction of generics added unnecessary syntax and bloat to Java code, but you never show the translation of Java 1.5 into Java 1.4. Namely, you neglect to mention the ubiquitous downcasts that must be performed in 1.4. Effectively, in your motivating loop code the type arguments on Map.Entry, <K, V> (sic), would mosey on down to typecasts on the accessor calls, like (String) e.getKey() and (Object) e.getValue(). Where is the extra bloat? Sure the colon might be harder to see, but the character count is roughly the same and you now have to make downcasts that could possibly throw exceptions.

The Ruby example looks cleaner first and foremost due to the closure. The sorted version of the loop is shorter in Ruby because of a convenient method in the collection libraries (surely there are almost-as-convenient methods in third party Java libraries like the PLT Utilities); admittedly there is an unnecessarily verbose type argument application when creating the TreeMap, but this would be moot with a sort method.

You also suggest that Java 1.4 was (is?) appropriate for day-to-day programming and not just for secure, type safe, thread safe software, while Java 1.5 is not -- due to generics. Maybe if 1.4 had closures and pattern matching and 1.5 gutted those, then I could see there being such a transition away from its utility in daily programming tasks.

To conclude: just install Scala.

Fred Finkelstein

Posts: 48
Nickname: marsilya
Registered: Jun, 2008

Re: Have Generics Killed Java? Posted: Jul 22, 2010 3:10 AM
Reply to this message Reply
>> To conclude: just install Scala.

It's interesting that the person who built Generic Java (which was incorporated, essentially unchanged, into the official Java language version 5) is the person who designed Scala: Martin Odersky.

Ronald Tetsuo Miura

Posts: 22
Nickname: ronaldtm
Registered: Jan, 2004

Re: Have Generics Killed Java? Posted: Jul 22, 2010 3:24 AM
Reply to this message Reply
1. All examples used in critics on generics compare collections processing between Java and Ruby (specially Maps, which get two type parameters). Ruby has collection processing built deeply into its syntax and APIs, just like all these 'functional-ish' languages. In many of these languages objects are basically, glorified maps. If you compare this specific feature, it's *obvious* that Java will lose every time.

2. In Java, most of this kind of code would be encapsulated/isolated (in utility classes), so that it won't mess with readability of the meaningful code. Well, unless it's crappy code, that you can write in any language.

3. Generics are OPTIONAL!!! If you think that in a particular block of code the type-safety is not worth the readability loss, don't declare the type parameters! You may then want to add a @SuppressWarnings("unchecked") annotation to the method/class, though. I actually do it a lot with Wicket code, because most of the time the type parameters on components (since 1.4) give me nothing but verbosity (the binding is done by reflection, implicitly, so there's no parameter for the type parameter to match with).



This kind of critics always ignore the power that tools, only viable due to static typing, provide. I'm doing a lot of Groovy code nowadays, and it's really frustrating not being able to refactor and navigate through code like I can do with Java. This is also the very reason I still just can't jump into Scala right now.

I've read somewhere, "It's just text". Well, it's not "just text", it's a model, and having, and knowing how to use, the tools allow me to manipulate this model effectively.

Ronald Tetsuo Miura

Posts: 22
Nickname: ronaldtm
Registered: Jan, 2004

Re: Have Generics Killed Java? Posted: Jul 22, 2010 3:28 AM
Reply to this message Reply
> It's interesting that the person who built Generic Java
> (which was incorporated, essentially unchanged, into the
> official Java language version 5) is the person who
> designed Scala: Martin Odersky.

He couldn't do better with Java due to, among other things, the backwards compatibility constraint. And that frustration is what led him creating Scala :)

http://www.artima.com/scalazine/articles/origins_of_scala.html

Morel Xavier

Posts: 73
Nickname: masklinn
Registered: Sep, 2005

Re: Have Generics Killed Java? Posted: Jul 22, 2010 4:18 AM
Reply to this message Reply
Eric, I don't think many are going to be able to reply to your post because it's fractally wrong. Pretty much everything in it is wrong including the premises, each and every argument and the conclusion. But to correctly reply to it would need a complete deconstruction taking hours, and I would guess most people don't have the time, drive and money to do that.

Instead, I will just say the following: Haskell is statically typed (with generic types of course) and is often terser than Ruby. What you demonstrate here is not that generic types or static types are bad, just that Java is a crummy language which has been completely unable to evolve gracefully.

> Generics force the programmer to do at something the computer is entirely capable of doing at run time (type verification).

No. That makes no sense. Generics let the compiler have a much deeper understanding of what happens and check types at compile time instead of doing them at runtime. They're a prime example of failing early, and in Java or C# avoid having to fill the program with type casts.

> Of course, runtime type checking makes your code more "unsafe", so you have to do more testing. But when you're not spending all of your time adding syntactic "sugar" to make the compiler swallow your code, you have a lot more time to make sure your code is doing the right thing.

So instead of spending some time specifying types and then let the compiler ensure you're not doing things that make no sense, you take that time and spend it writing tests which do the exact same verifications?

Fire Mage

Posts: 3
Nickname: firemage
Registered: Jul, 2010

Re: Have Generics Killed Java? Posted: Jul 22, 2010 4:56 AM
Reply to this message Reply
I understand your frustration with generics. Every time I have to type in type parameters while coding it seems to put the brakes on my chain of thought and create a small mental disturbance akin to a "... not that again". But claiming that "generics kill Java" is exaggerated (proven by the fact that 6 years after generics, Java is still the most widely used language).

First, as another user pointed out, you don't have to use generics. You can code on as if they never existed. Also as another user pointed out, generics basically only replace the type cast by a compile time check. Java is a statically typed language, there is no way around that.

I think what your post boils down to is statically vs. dynamically typed languages and that you think that the latter are inherently superior.

Of course dynamically typed languages are nice. I have worked with Python for a while and I practically never experienced the missing type checking as a problem. It was also a very liberating experience. After all, if I -the programmer- know that there are only Strings in that list, why do I have to cast them? Can't the compiler just trust me on this?

And the answer is no. An interpreter for dynamic languages can, but not a compiler. Distrusting the code is its job.

Dynamically and statically typed languages occupy different niches and they each have their specific point and purpose. If you're writing short, concise programs all by yourself, then by all means, go dynamic and forget about types. The responsibility to do the right thing lies with you alone.

But if you're working with 20 people on a big project you don't always know what other codes intended. Coding this without static type checking is asking for trouble. Also, static type checking makes it possible to use tools and methods, such as static analysis, that are impossible to apply when working with a dynamic language.

I would say that your "elegance=power/characters" formula is a little one-dimensional, because elegance is not the only important thing in a PL. That is the whole point. Other important factors are "scalability", "ability to work in team", "tool support", "safety"... in all of these a statically typed language will be superior. There is a reason why dynamic languages are mainly used for scripting, usually embedded into a statically compiled language.

Fire Mage

Posts: 3
Nickname: firemage
Registered: Jul, 2010

Re: Have Generics Killed Java? Posted: Jul 22, 2010 5:29 AM
Reply to this message Reply
>the following is scheduled for
> JDK7:
>
>
> List<String> strings = new ArrayList<>();
> 

>

This is certainly a welcome change.

>
> strings.sort().each( { s -> out.println( s ); } );
> 


I think that Java needs to be careful not to cater to the dynamic community too much, especially as it is unnecessary, since many dynamic languages run under the Java VM. I think that Java is feeling the competition and is trying to hard to appeal, thereby introducing new syntactic sugar that is however "unpure". For example, I particularly dislike the automatic type boxing feature:

Integer i = 12; or
int i = new Integer(12);

Yes, it's shorter . More "elegant" than Integer.valueOf(12) according to Erics formula, but it's also wrong and confusing to newcomers. 12 is a primitive type and Integer is an object, they are not equal. This can cause some subtle errors that are hard to notice. For example every time you have an overloaded method that accepts a primitive type and an object, such as in ArrayList:
remove(Object o);
remove(int index);

I had an Integer object "i" and was calling list.remove(i), expecting to remove the object at a specific index (thanks to autoboxing). Of course, the actual method being called was remove(Object) and nothing was being removed, because my Integer object wasn't in the list.

And this is the point. Autoboxing fools you into thinking that 12 and "new Integer(12)" are the same, whereas they are something entirely different. I hope Java won't introduce any more of these "fuzzy" constructs.

John Wellbelove

Posts: 72
Nickname: garibaldi
Registered: Mar, 2008

Re: Have Generics Killed Java? Posted: Jul 22, 2010 7:00 AM
Reply to this message Reply
>> Have Generics Killed Java?

One can only hope ;-)

Morel Xavier

Posts: 73
Nickname: masklinn
Registered: Sep, 2005

Re: Have Generics Killed Java? Posted: Jul 22, 2010 7:05 AM
Reply to this message Reply
> I think that Java needs to be careful not to cater to the
> dynamic community too much

Where does it do that so far? Since when is introducing first-class anonymous functions "cater[ing] to the dynamic community" at all, let alone too much?

> I think that Java is feeling the competition and
> is trying to hard to appeal, thereby introducing new
> syntactic sugar that is however "unpure". For example, I
> particularly dislike the automatic type boxing feature:

That's because it's crummy, as usual in Java.

> Integer i = 12; or
> int i = new Integer(12);
>
> Yes, it's shorter . More "elegant" than
> Integer.valueOf(12) according to Erics formula, but it's
> also wrong and confusing to newcomers. 12 is a primitive
> type and Integer is an object, they are not equal.

And that is very specifically one of Java's worst misfeatures. `int` and `Integer` should be aliases. And should always have been.

> And this is the point. Autoboxing fools you into thinking
> that 12 and "new Integer(12)" are the same

Which they should be.

> I hope Java won't
> introduce any more of these "fuzzy" constructs.

And I hope Java deprecates and removes all the gunk in the language. As far as I am concerned there is no reason for having both int and Integer, everything should just be `int` and Java does its boxing when needed (method call on an int or whatnot)

Flat View: This topic has 62 replies on 5 pages [ 1  2  3  4  5 | » ]
Topic: How Much Unit Test Coverage Do You Need? - The Testivus Answer Previous Topic   Next Topic Topic: Are Dynamic Languages Going to Replace Static Languages?


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us