Sorry, I'm in a bad mood today. It must be an uphill battle I guess. I've seen people confused with generics (I'm talking about Java here). They seem to get the concept and can use generic collections pretty well. Once teached they happily write:
Set<Something> somethings = new HashSet<Something>();
And for some unforeseeable reason they're happy with themselfs and proud of using generics. Some of them even attempt to understand advanced concepts like erasure (a concept that shouldn't even exist by the way). And, in fact, they do. Now, that's the end of the story. A dead end indeed.
For the great majority generics are something, revolving collections (and comparators..or was that TOO advanced?), made by Sun so everybody can use them later in their applications. Nobody seems to go one step beyond and try to develop generic classes of their own. Go figure!
So say we are designing a client application that needs to connect to several types of servers. A downright mediocre architect would write:
For the great majority generics are something, revolving collections (and comparators..or was that TOO advanced?), made by Sun so everybody can use them later in their applications. Nobody seems to go one step beyond and try to develop generic classes of their own. Go figure!
So say we are designing a client application that needs to connect to several types of servers. A downright mediocre architect would write:
public interface Server {
Object openConnection(Object connectionDetails);
}
Object openConnection(Object connectionDetails);
}
He will argue that the specific types are unknown at the moment. Utter crap, of course. While an old school, barely capable, one would instead put something like:
public interface Server {
Connection openConnection(ConnectionDetails connectionDetails);
}
Connection openConnection(ConnectionDetails connectionDetails);
}
Better no doubt. But it suffers the same issue, namely downcasts. Downcasts are obviously not elegant but they also pinpoint problems and bad practices. Avoid like the plague.
Generics to the rescue. Yes, you can do better! Believe me! That's what generics are really for. Not for collections but for you! For your designs! Let's refactor that interface of us a little. What would we need? Well, we know that a connection to the server is needed and that the server implementation and hence its connection are only known by the subclass in particular. So a FTP client has to return a different connection object that a Subversion client. Fine. Starting were our old good architect left the code we could try to modify it a bit. For example:
Generics to the rescue. Yes, you can do better! Believe me! That's what generics are really for. Not for collections but for you! For your designs! Let's refactor that interface of us a little. What would we need? Well, we know that a connection to the server is needed and that the server implementation and hence its connection are only known by the subclass in particular. So a FTP client has to return a different connection object that a Subversion client. Fine. Starting were our old good architect left the code we could try to modify it a bit. For example:
public interface Server<T extens Connection> {
T openConnection(ConnectionDetails connectionDetails);
}
T openConnection(ConnectionDetails connectionDetails);
}
I know what your going to say...does that even compile? But looking at it more closely the changes are really minimum. We are just declaring a generic server that has one unknown parameter. But not completely unknown , at least we know it will be some kind of Connection object. But didn't we have that already covered with the old example? Yes..and now. Let's see an implementation. First we are going to use an abstract base class with some minor checkings:
public abstract class AbstractServer<T extends Connection> implements Server<T> {
protected abstract T doOpenConnection(ConnectionDetails connectionDetails);
public final T openConnection(ConnectionDetails connectionDetails) {
..do some checks...
return doOpenConnection(connectionDetails);
}
}
protected abstract T doOpenConnection(ConnectionDetails connectionDetails);
public final T openConnection(ConnectionDetails connectionDetails) {
..do some checks...
return doOpenConnection(connectionDetails);
}
}
The only interesting bit of the above code is the way we carry the generic parameter down the hierarchy. Till when? Until the final Server implementation:
public final class SubversionServer extends AbstractServer<SubversionConnection> {
public SubversionConnection doOpenConnection(ConnectionDetails connDetails) {
...
}
}
public SubversionConnection doOpenConnection(ConnectionDetails connDetails) {
...
}
}
See the difference now? The openConnection returns a specific class so the compiler checks the types for us! No more downcasts in the process! No class cast exceptions at runtime. And finally something to be proud of to show to your colleagues ;-)
PS.- In case you're wondering where that code actually comes from I'm happy to announce that I've started a new Open Source project at Google Code: IWebJTracker. It's in its infancy right now but with time it will become an issue (project?) tracker built completely in Java. Think of Trac or Project Tracker. Why I'm doing it? For two reasons: first so IWebMvc has a poster child application, second because there are no good OSS Java issue trackers. And by good here I mean with good Maven integration..Sonar...Hudson...see where I plan to go?
PSS.- Any help is gladly welcome :-)
PS.- In case you're wondering where that code actually comes from I'm happy to announce that I've started a new Open Source project at Google Code: IWebJTracker. It's in its infancy right now but with time it will become an issue (project?) tracker built completely in Java. Think of Trac or Project Tracker. Why I'm doing it? For two reasons: first so IWebMvc has a poster child application, second because there are no good OSS Java issue trackers. And by good here I mean with good Maven integration..Sonar...Hudson...see where I plan to go?
PSS.- Any help is gladly welcome :-)
