Jul 23

Java Complexity

Java as a lan­guage is get­ting more com­plex with each ver­sion. From ver­sion 1.0 to 1.4 most of the com­plex­i­ty was added through libraries. Thousands or lines of code were added in hun­dreds of new class­es. But this com­plex­i­ty was contained–if you did­n’t need a fea­ture you could ignore it com­plete­ly. At worst, you might miss out on some new, neat fea­ture. But start­ing with 1.5 the lan­guage itself changed (OK 1.4 added asser­tions but that’s a very small fea­ture that can be read­i­ly ignored).

Version 1.5 added enums and Generics. Enums are a medium-​sized change, main­ly a way of list­ing enu­mer­a­tion val­ues, but there are sub­tleties that make them com­plex (like con­ver­sion to/​from inte­gers, access­ing all the defined val­ues, etc.) Generics are a big change. The fact that all the col­lec­tion class­es were retro­fit­ted to be gener­i­cized1 means that most new code will have to deal with them whether they want to or not. Trying to ignore them just results in streams of warn­ings.

Now with 1.7 or maybe 1.8 we are talk­ing about adding clo­sures and pos­si­bly con­tin­u­a­tions and prop­er­ties. If you think that code with gener­ics is hard to read then just wait for clo­sures. The sub­tleties of where a braces goes to decide if a clo­sure is being defined is guar­an­teed to cre­ate uncer­tain­ty and con­fu­sion.

My ques­tion is “are we adding too much com­plex­i­ty?” The thou­sands of lines writ­ten for the libraries before 1.5 are extreme­ly use­ful and in fact are still used after 1.5 so gener­ics, enums, and clo­sures are not nec­es­sary for use­ful code. So why do we see the need to add this com­plex­i­ty?

When I start­ed using Java I thought it was a nice sim­ple lan­guage for writ­ing web tools but that it was­n’t a “real” lan­guage for use in “real” work. But then I start­ed using it for a real project and I real­ized that it might be a sim­ple lan­guage, but it could do what I need­ed. Granted pre‑1.2 things were a lit­tle coarse but by the time 1.2 came out things real­ly start­ed to smooth out. Then as I used it more I kept think­ing “this is a bor­ing lan­guage; it has no fun fea­tures.” Years lat­er, as I have got­ten old­er, I have begun to recon­sid­er my eval­u­a­tion. Yes, Java is a rather sim­ple lan­guage, but that may be a good thing.

I like func­tion­al lan­guage as I think that func­tion com­po­si­tion is a very pow­er­ful fea­ture that results is small­er code bases. But when I look at what’s com­ing in future releas­es I start to wor­ry. Here where I work we have devel­op­ers of dif­fer­ing lev­els and I have seen them have prob­lems with even the sim­ple things in 1.4. Complex fea­ture like Generics are going to make it hard to keep every­one up to the nec­es­sary lev­el of com­pe­tence.

Even though I was one of those clam­or­ing for Generics I am now not so sure that they should have been added to the lan­guage. I con­sid­er myself a senior devel­op­er and while I am now com­fort­able, but not an expert, with using gener­ics it is such a big change that I would say that Java 1.5 is actu­al­ly a new lan­guage. As a senior devel­op­er I have no prob­lem learn­ing new lan­guages and in fact I learn new ones for fun. But think of the junior and even mid-​level devel­op­ers. May of them are still try­ing to get a han­dle on the base lan­guage.


  1. I know this isn’t a real word yet, but it is becom­ing preva­lent in the busi­ness and it seems to be the right word for this sit­u­a­tion. 
Jul 17

Java Generics III

Last time I talked about using gener­ics to make get­ting val­ues out of col­lec­tions nicer (and a pro­pos­al that would obvi­ate their use) so this time I want to talk about the oth­er half–passing items into a col­lec­tion. This encom­pass­es all meth­ods that take a para­me­ter of the col­lec­tion’s gener­ic typ­ing includ­ing those that add items to the col­lec­tion.

If you look at the byte-​codes gen­er­at­ed for call­ing any of these meth­ods you will see that there is no run­time check­ing of the objects being passed. This is because the meth­ods of the base object are defined as tak­ing Object types. The only check­ing hap­pens at com­pile time. So as long as the sta­t­ic type is cor­rect every­thing is fine but it is easy to over­ride the sta­t­ic type (acci­den­tal­ly or on pur­pose) so what hap­pens? Well, if you attempt to retrieve a val­ue from the col­lec­tion and the type is not assign­ment com­pat­i­ble with the tar­get then you will get a ClassCastException.

Let’s think about that for a moment. The excep­tion is not thrown when the invalid val­ue is added to the col­lec­tion (when track­ing down the error would have the con­text of the sit­u­a­tion) but when it is removed (or exam­ined). This defeats the basic rule of “fail as ear­ly as pos­si­ble”. It also means that a state­ment with no cast oper­a­tor can fail with a class cast excep­tion. This seems counter to the spir­it of the lan­guage and in fact leads to con­fu­sion. You look at the line that the stack trace points to and say to your­self “there is noth­ing there that can throw a class cast excep­tion.” After a this hits you a time or two you will remem­ber but why should you have to make that effort?

I will grant that the gener­ic def­i­n­i­tion does make it hard­er to acci­den­tal­ly put in the wrong thing, but it does­n’t elim­i­nate it entire­ly. If we are going to have a checkcast byte-​code on retriev­ing the val­ue then why don’t we have it on putting the val­ue in as well?