Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Thursday, July 07, 2005

<> vs. () makes me :-(

All I wanted was fewer casts. I'm sick of typing all those casts (they tend to get out of control when using EasyMock). The compiler knows enough about the left and right side of my assignments to require a cast, why can't it just provide it (this is the Dave Thomas observation).

Now, in an attempt to simplify things, we've replaced casts with generic type constraints. So instead of:

Map _map = new HashMap();

public RegistrationData getRegistration(String userId)
  return (RegistrationData) _map.get(userId);
With generics we get:
Map<String, RegistrationData> _map = new HashMap<String, RegistrationData>();

public RegistrationData getRegistration(String userId)
  return _map.get(userId);
So, much more repetition and uglier code to avoid that one little cast. A cast the compiler could just as easily have provided for me? One that still exists at runtime (that's type erasure). This is simpler?


Guillermo said...

I don't think generics were created to simplify things. The main improvement in generics is the fact that now casting is done in compile-time, which means that errors will be easier to catch.

In the example code you mention, you have no assurances that the object returned from the map is really a RegistrationData in the first case, but you will be 100% certain in the second case.

afsina said...

1- you dont have to use generics if you do not like the way it looks.
2- the case you show is just a one liner, an extreme case. usually generics makes the code simpler. i think safety is much important anyway.

Unknown said...

Yes and no. What burns me is that when I start to pass an genericized object around, those type qualifiers, inside the < and >, keep coming back. I believe you can create an interface to consolidate that.

The mantra of compile-time safety is ebbing. This is why Ruby is starting to kick Java's ass. I've been talking about the difference between "Pragmatic" languages (Ruby) and "Dogmatic" languages (Java).

I chose "Dogmatic" carefully; Java has an architect, Gilad Bracha, whose title is "Chief Computational Theologist".

Because of type erasure and the like, you still can't be sure that the code above doesn't throw a ClassCastException (an assignment to _map using a cast would do it, because the type qualifiers are "erased").

More importantly, compile-time safety is a bit of a fraud given that you're going to write tests. And the tests will not throw ClassCastExceptions. So if the compiler simply injected the casts for us, invisibly, we'd be at the same point ... with much more readable code.

Smalltalk IDEs still blow away Java IDEs in terms of refactoring DESPITE the lack of compile time type information. Again, Pragmatism vs. Dogmatism.

Generics are a big experiment of questionable value that has been unleashed on us all, ready or not. It can't be removed (Sun never removes anything from Java), so we're stuck with it, like it or not, forever.

I used to scoff at the notion that "Java is the new COBOL". Not anymore.

alang said...

In my (not very wide) experience, I end up typing class names slightly fewer times with generics than with casts. This is because code tends use variables more often than it declares them. YMMV.

On the whole, I think generics are a good thing for Java, even if they are not as amazingly good as the original evangelistic literature promised.

Trying to be pragmatic, my response is to embrace generics when programming in Java on one hand, while using my other hand to find projects that don't require Java at all. And it sounds like you're ahead of me on that score.

Unknown said...

Instead of generics they should've implement type inference in java.
This way you still would get most of the pros of generics including comile time checking and greatly simplify coding.

Type inference is implemented in such static typed languages as Haskell and Clean.

Nick Stuart said...

One thing though to keep in line with the DRY principle. You dont need the second set of <>.

You could simply do this instead:

Map<String, RegistrationData> _map = new HashMap();

Which is much cleaner and easier to read. Also, there is nothing stopping you from casting from say a regular map and a map<> (assuming of course the types are the same.)

kytol said...

I think you're missing the point of generics.

Generics are about type safety, not merely about removing the need for casts.

Sure, without generics the compiler could do an automatic cast, but this isn't going to remove the possibility of ClassCastExceptions being thrown when items are removed.

Generic ensure the items in the collection must of the expected type.

Bruce said...

I really hope that dynamic languages like Ruby continue to make inroads in mainstream development, but for better or worse Java is here with us to stay. Not only that but Java has millions of lines of code in thousands of commerical and freely available libraries and frameworks that it would be a shame to have to toss out. Fortunately, there is a solution. Languages such as Nice (http://nice.sourceforge.net/) provide features such as automatic type-inference, multimethods, named arguments, closures, etc. but compile down to Java bytecodes. So toss (or transcend) the crappy language and keep the JVM. Problem solved.

Bill said...

So port Tapestry to smalltalk :)

Charles Miller said...

Is it just me, or does the subject line of this post end up looking like a big ASCII fish?

Erik said...

Perhaps it is time to do some reuse from the good ole' C/C++ days.

There the typedef keyword was available.

So after declaring

typedef Map<Integer,List<Person>> PersonMap, we could simply use PersonMap to refer to the type instead of retyping the expression every time. Strange they did not add it to 1.5.