Showing posts with label rant. Show all posts
Showing posts with label rant. Show all posts

Friday, January 04, 2008

Java Generics Broken? We Report, You Decide.

Since I've been taken to task in the past for my cogent observations of Java's, um, shall we say "unintuitive" behavior, particularly in the era of generics and autoboxing, I'm just going to put this one out there without making any (public (explicit)) value judgments.

So it seems that if you define a method thusly (note: requires Java 5 ("Gangly Geek") or better greater having a numerically higher version number):
public void doSomethingGenerically(Collection<Supertype>){...}
and you attempt to call it thusly:
List<SubtypeOfSupertype> bogus = new ArrayList<SubtypeOfSupertype>();
doSomethingGenerically(bogus);
you get a compiler error saying you can't call that method with those arguments.

So my question here is: why the heck not?

Inheritance 101: a List isa Collection, right? A SubtypeOfSupertype isa Supertype, right? So why am I being told that an instance of type List<SubtypeOfSupertype> isn'ta Collection<Supertype>?

Paging Dr. Liskov...

Tuesday, August 07, 2007

Autoboxing == !Cool

So you're finally getting used to the idea of Java 5's "feature" of converting between primitive types and their corresponding object wrappers (e.g., int -- Integer). You're getting to the point where you kind of trust it. You're even getting to the point where you kind of rely on it. Life is good.

Then you go too far and try something weird and exotic like, oh, I don't know:

int sucker = thisMethodReallyReturnsAnIntegerNotAnInt();

No squiggly red line in your Eclipse editor. Cool! That must mean this will work! Because Java has static typing, and you can count on that! Yea you!

The problem is, it will work. It will work very nearly all the time. It will work until your method returns a null instead of an actual Integer. At which point you'll get a NullPointerException buried in your Struts 2 / OGNL / JSP project's Eclipse console stack trace, followed by a two-hour detour into the debugger wondering what the heck is going on. I mean after all, your code compiled, right? It worked for the first hour, right? Static typing is good, right? Autoboxing works, right?

Not that you're bitter.

Tuesday, June 05, 2007

Rant: JSP + OGNL + Collections == Train Wreck

The Story So Far

In my day job I'm using Struts 2, with JSP as the view templating mechanism. I have a collection whose size I'd like to report on the page. Unfortunately for my sanity, I have recent experience with Ruby on Rails, in which such a thing is as simple as:

<%= @myCollection.size %>

But no.

Problem #1

Struts 2 exposes properties on the Action class via OGNL, which has a nice, clean, property-based syntax, much like RHTML (which is what Rails uses for its view templates). So I should be able to ask for something like this:

${myCollection.size}

Assuming, that is, that I have a method getMyCollection() defined on my action. Which I do.

The problem here is that I also have to have a method called getSize() defined on whatever getMyCollection() returns. Which is a java.util.Set. Which, for some reason, does not have a getSize() method. It has a size() method instead. Apparently the designers of the Java 2 Collections API were feeling a mite saucy when they went a-designin', and were daring their overlords to punish them for ignoring the Java Beans method naming convention that, as it turns out, OGNL relies on heavily.

D'oh!

No problem, though. OGNL doesn't require method names to be bean-compliant, it just prefers it in its chain of figuring out what the heck you're asking for. You can invoke any method directly, as in:

${myCollection.size()}

Problem solved! Let's save everything and reload:

Struts Problem Report
Struts has detected an unhandled exception:
Messages:
view.jsp(40,109) The function size must be used with a prefix when a default namespace is not specified
org.apache.jasper.JasperException: view.jsp(40,109) The function size must be used with a prefix when a default namespace is not specified


What the...?! Oh. The JasperException must mean that the ${...} expression is being interpreted as JSP EL instead of OGNL. Bummer. Oh well. I'll just let the EL engine handle it.

Which brings me to:

Problem #2

The JSP EL engine can't handle it. Neither the property syntax nor the method syntax works. The property syntax wants to use the (non-existent) getSize() method too (go figure). And the method syntax doesn't exist.

So. Since it looks like JSP EL trumps OGNL when the page is rendered, I'll just turn off JSP EL evaluation (in web.xml) and let OGNL handle everything.

Or not. That results in a ton of errors from pages in my app that rely on JSP EL.

Solution

I'll try to wrap this up. It turns out that the answer is JSP functions. "All" I have to do to get the number of items in my collection is write a public function class and implement a static method -- which I get to name anything I want! Just like the Collections API designers! -- that takes the collection as a parameter and returns its size.

Oh, and I have to write a snippet of XML in the form of a .tld file that tells the JSP where to find this function.

Oh, and I have to declare the .tld as a taglib at the top of the JSP. And that's all I have to do.

Sheesh.

Conclusion

Fortunately, it turns out that the hardest part of this work has been done for me, in the form of the JSTL implementation (documented here) of several useful JSP functions, including length().

It doesn't help my mood any that in Ruby -- if the problem existed to begin with, which it doesn't -- I could have solved it simply by adding a getSize() method, aliased to the size() method of the Set class, and everyone would be happy.

Grumble.