Wednesday, 24 Sep 2003
Functional Style in Java
Functional programming is (in part, to oversimplify) a way of writing
programs using only immutable variables and objects. This eliminates
certain kinds of bugs that happen when objects change unexpectedly.
Normally, functional programming is done in specialized languages like
ML and Haskell that aren't very popular. I believe this is because
the programs tend to be rather mathematical and use special,
hard-to-understand tricks to get work done despite the illusion that
nothing actually changes. They also don't have the vast libraries and
tools that mainstream programmers are used to.
However, it's possible to apply some of the lessons from functional
programming to Java (and similar languages), and I believe it results
in a pretty good coding style that can be easier to debug than your
average JavaBean.
The first step is to use final instance variables whenever it makes sense.
This is an overlooked Java language feature that even experienced Java
programmers might be surprised to learn about. For a simple example,
here's a record in a music database:
public class Album {
private final int albumID;
private final String albumName;
private final Artist artist;
private final List tracks;
public Album(int albumID, String albumName, Artist artist, List tracks) {
if(albumID<1 || albumName==null || artist==null || tracks==null) {
throw new IllegalArgumentException("null arg"); // (simplified)
}
this.albumID = albumID;
this.albumName = albumName;
this.artist = artist;
this.tracks = Collections.unmodifiableList(new ArrayList(tracks));
}
...
}
If you're not familiar with final instance variables, this might look
a little odd. Don't you have to set a constant when you declare it?
It's a special rule: a final instance variable must be initialized
exactly once, either when declared or in the constructor.
The nice thing about final instance variables is that if you've
verified an invariant in the constructor, you can be certain it holds
for the life of the object. No matter how complicated this class gets
or what any other ill-considered method does, albumName,
artist, and tracks can never be null. Anyone reading
the class can easily verify this by looking at the constructor. It
makes debugging much easier. You don't have to worry about
synchronization and you don't have to do null checks. And (not that
I'm recommending this), you could even make the instance variables
public and get into surprisingly little trouble.
Of course, immutable objects are nothing new (see String), and you can
write one without using final instance variables. But then bugs can
break the invariant and you have to read the whole class to be sure
it's really immutable. (For protected variables, it's even worse -
you have to read all the subclasses.)
It's useful to make classes partially immutable, so this isn't an
all-or-nothing strategy. The goal is to reduce the complexity of the
system. (If you're retrofitting an existing system, a good start is
to make id's immutable.)
A mechanical device might be advertised as having "only 3 moving
parts" to emphasize its simplicity and reliability. A non-final
instance variable is a moving part. How many moving parts do you need
to write your class? Could you get away with fewer?
respond | link |
/code
|