Showing posts with label generics. Show all posts
Showing posts with label generics. Show all posts

Friday, May 23, 2008

PECS in Action

One funny bit at JavaOne was Josh Bloch introducing the mnemonic PECS. Well actually what was most funny about it was the picture of Arnold Schwarzenegger that accompanied it... Anyways, PECS stands for producer-extends, consumer-super. Instead of repeating what Josh said or plagiarizing Effective Java, I will give an example of how PECS helped me out yesterday.

One there was an API that existed back in olden times, before Java 1.5 It looked like this:

void runInboundCycles(final Module[] modules)

There was also a runOubound, but you get the picture. The class Module is an interface that has many implementations. This API got tweaked courtesy of Java 1.5:

void runInboundCycles(final List<Module> modules);

Before the change you could do this:

runInboundCycles(new Module[] { new MyModule() } );

A logical uprev would be:

runInboundCycles(Arrays.asList(new MyModule()));

Turns out that won't compile! The Arrays.asList call will return a List and the Java compiler says that is not a List. So instead you have to do something annoying like:

List<Module> modules = new ArrayList<Module>(1);
modules.add(new MyModule());
runInboundCycles(modules);

This is particularly annoying if modules is actually a member variable, as now you cannot declare it to be final. Enter PECS.

My API is using the Modules, thus my parameter is a producer to the API. Remember producer-extends, so refactor the API like this:

void runInboundCycles(List<? extends Module> modules);

Now you can pass a List<MyModule> and the compiler won't complain.

There are a couple of things about this that bother me. When my crazy brain looks at the API, it thinks "the API consumes Modules." Maybe that's just me. The other thing that bothers me is writing ? extends Module because Module is an inteface. Now granted it would suck to have to write ? implements Module just because Module is an interface and write ? extends Module just because Module was a class, so I am not advocating the altnernative. It just feels weird to write extends in front of an interface type. Maybe I have been programming in Java too long.

Sunday, May 18, 2008

How do You Use Generics in Java?

Cedric has fired up some more flames in the never-ending dynamic vs. static language wars. I won't bother with all of the obviously flammable objects in that post, but instead with this quote that I found really surprising:
90% of the Java programmers (including myself) only ever use Generics for Collections.

Is this true? It's not for me, obviously or I wouldn't have been surprised by it. Generics are so useful beyond collections. One of the most commonly implemented interfaces I deal with looks like:

interface Service<R,S> {
   S processRequest(R request);
}

This is a very simple use of generics to add type safety to a common construct. What would this look like without generics? Either everything is an Object, or there are some wrapper interfaces that would have some kind of getData() method that returns ... an Object. It's not type safe but nothing else could be reusable. 

A lot of people think that Java took a wicked turn with the 1.5 release, and generics are probably the biggest reason for this. To me, the key was if you had programmed in C++ before or not. If you had, then generics seemed natural. It was definitely not the same as C++ templates, but the concepts were similar and the syntax very similar. If you had not experienced C++ templates, then generics were terrifying. I would guess that camp probably would agree with Cedric's post, i.e. they only use them for collections.

Of course the next great scary Java feature is closures. The BGGA proposal leverages generics to specify checked Exceptions, such as below:

public static <T,throws E extends Exception>
T withLock(Lock lock, {=>T throws E} block) throws E { ... }

Even scarier? Maybe so... Anyways, I am genuinely curious how other folks use generics. Is it just for collections?

Tuesday, February 27, 2007

Generics Puzzler

I just read this Java generics puzzler from Pure Danger Tech. I have felt exactly this pain many times before. The decision to erase types from compiled bytecode causes so much pain. I don't think .NET uses type erasure, so I figured this would be pretty easy in C#:


static T[] merge<T>(params T[][] arrays)
{
int len = 0;
foreach (T[] array in arrays)
{
len += array.Length;
}
T[] result = new T[len];
len = 0;
foreach (T[] array in arrays)
{
foreach (T elem in array)
{
result[len++] = elem;
}
}
return result;
}

Thursday, January 18, 2007

Java Properties and Generics

If you've done much Java programming, you've probably used a Properties object at some point. It's been around since JDK 1.0. It's always been backed by a Hashtable. With Java 5, the Hashtable became genericized, just like all the other Java datastructures. The Properties object was always "genericized" in a way. It's getProperty and setProperty methods only took and returned Strings. So it would seem logical that Properties would extend a Hashtable<String,String>, right?

Nope, it's still a Hashtable<?,?>, i.e. a Hashtable<Object,Object>. Where is all this going? Well I had a class that had a Properties object as a member variable. I had a method that needed to return all the property names of that Properties object. I thought it would make sense for my method to return a Collection<String>. Since Properties extends Hashtable, I could just return it's keySet() method. This is a Set of all the keys in the Hashtable backing the Properties, thus it is a Set of Strings (Set<String>) and thus a Collection<String>. Too easy.

Wrong again. The return type of the keySet() method is a Set<object>, because Properties extends Hashtable<Object,Object>. You cannot case a Set<Object> to a Set<String>. I was very surprised by this. I expected to have throw a @SuppressWarning("unchecked") annotation on my method, but I did not expect that it would be a compilation error. I figure this is because I would be casting a generic interface. I guess. I could do something completely ridiculous:

return (HashSet<String>)new HashSet(myProps.keySet());

This worked with a good 'ol @SuppressWarning("unchecked") annotation. I had to copy my keySet() into a concrete Set object, and then I could cast. Totally ridiculous.

This all came because my Properties object was being injected with Spring. In other words I had a Spring context file with something like:

<property name="myProps">
<props>
<prop key="a">1</prop>

...

I simply replaced this with a map:

<property name="myProps">
<map>
<entry key="a" value="1">
....

I could then make myProps into a Map<String,String> instead of a Properties object, and simply return it's keySet() without any casting. Very nice. So this just brings me back to the original question of why doesn't Properties extend Hashtable<String,String>?