Explicitly and without apology a marketing vehicle MaryMaryQuiteContrary

Monday Sep 27, 2004



It's a zip-a-dee-do-da day, today,  people.

Spectacular weekend. Beautiful weather. Played outside a lot. Went to a couple parties -- one featured a moon bounce and a fire truck; the other a champagne fountain. Saw a bonafied techno celeb and celebrated author who happens to run in the same social circles as me, if you can even believe that.

Had book club yesterday -- and I actually read the book. (abbreviated review: Book of Salt. Beautifully written. I envy people who can manipulate the English language with such skill. It was a downer, though. Totally depressing. I don't understand why these brilliant authors can't write books that make you laugh. Why does it always have to be about misery?)  The most fascinating women are in my book club. They are so well read and well traveled. I love talking to them.

I'm just in a really, really good mood. I'll let you guess what else I did.

Slight downer from the weekend: So at church yesterday Number 2 flat out refuses to put the money I had given her for the collection into the tray. This created the perceived inequity on the part of Number 1, who had already put her money in and now felt cheated. (I think Number 1 was actually entertaining the idea of reaching back into the tray to get her dollar back, but thankfully didn't act on that impulse)  Both children (loudly) articulated how they were feeling. Not a pretty scene. I try to roll with it. I try not to feel embarrassed and mortified. with mixed success.

anyway...

We've got a loop to close and then I've got to jam.

I've got four working days left in the month of September and I've got to quick get something done so that I can put it on my monthly report. (Just kidding, Ingrid. a little humor there... just a little joke..)

1. Please join me in congratulating Tom, who wins this spectacular package of prizes...

package of prizes

Tom is the official Puzzler Solver and Friday Free Stuff winner. Satish gets some free stuff (expectation set: t-shirt) too because he's second runner up. Should Tom be unable or unwilling to perform his duties as Puzzler Solver and Friday Free Stuff winner, Satish will have to step in and wear the crown for the duration of this rein.

quick aside: I'm giving away a jacket! (can you even believe that?!) So if you're jealous because Tom got this prize package, there's a jacket on the table. If you're a Sun Certified professional for Java Technology, you've got the opportunity to receive from me a really, really, really nice bomber jacket. Check it out.

And just to close this one out... here's the solution to the Puzzler which landed Tom (and Satish) some pretty hot free stuff and the rest of  you the feedback from Dr. Josh that the readers of this blog are "very clever."

but i already knew that.

happy Monday!

mary

p.s. here's the answer to the puzzler...

Click and Hack apologize for their lack of punctuality. They had a crazy week. Tom Hawtin, on the other hand, was very punctual: He posted a correct solution to this week's Puzzler about an hour after it was posted! This is definitely a record, and Click and Hack tip their hats to Tom. Other correct solutions were posted later in the week--lots of clever folks out there!

As you'll recall the puzzler was to make a client of a final class fail to compile simply by adding a private member to the final class. (By "client" we meant a class in a separate package that invokes public constructors or methods of the class, or accesses its public fields.)

Tom's solution was this:

package fin;

public final class Final {
    //private interface Runnable { }
    public final class Runnable2 implements Runnable {
        public void run() {
            ;
        }
    }
}

package client;

class Client {
    public static boolean isRunnable(fin.Final.Runnable2 f) {
        return f instanceof Runnable;
    }
}
He added a private member class to Final named Runnable that shadows (JLS 8.5) java.lang.Runnable. Because he declared Final to implement Runnable, the addition of the private member class affected the publicly visible superclasses/interfaces of Final. Prior to adding the private member, they comprised java.lang.Runnable and java.lang.Object; after adding the member, only the latter remained.

In Tom's client class, he took advantage of the fact that the instanceof operator generates a compile-time error if a cast of its first argument to its second would generate a compile time error. Not too many people are aware of this fact, but Tom is. Nice solution, Tom, and quick thinking! Andrew Taylor's solution was, as he said "more or less the same as Tom's." Instead of shadowing a superinterface with a private member, his class shadowed a public method's return type, effectively changing its return type from java.lang.String to java.lang.Object (from the perspective of a client outside the package).

Satish Srinivasan's solution was also correct, and fundamentally different from Tom's:

package contrary.foo;

public abstract class FinalBase {
    public static final String FOOBAR = "foobar";
}

package contrary.foo;

public final class Final extends FinalBase {
  //private static final String FOOBAR = "ha-ha";
}

public class FinalClient {
    public static void main(String[] args) {
        Final thingy = new Final();
        System.out.println(thingy.FOOBAR);
    }
}
Satish's solution uses hiding (JLS 8.3) in place of shadowing. In other words the private member that he adds to class FinalBase makes a superclass member invisible. This is particularly nefarious, because it violates the subsumption, which is the principle that a subclass should do everything that its superclass can (and possibly more). In this case, Final does less than FinalBase: the latter has a publicly visible FOOBAR field, while the former does not.

Many people are shocked that the Java programming language permits a subclass field to hide a superclass field with greater access privileges. It is impermissible to override a method with one that has weaker access privileges, so why should fields be any different? Arguably this was a mistake in the design of the language, but such is life. Every language has its flaws, even great ones like Java.

A multitude of variants on Satish's solution are possible. The hidden member can be member class rather than a field, and it can be an instance member rather than a static member. Not only can you hide a field with one of lesser accessibility, but you can hide a field with one of a different type. With any of these variants, you can silently and inadvertently hide a public member (field or type) of a supertype in a subtype. Once you've done this, the subtype no longer supports the same API as its supertype. As you evolve a library evolves over time, you can break previously valid client code by adding a private members. To prevent this, compilers should generate warning messages whenever this sort of hiding occurs. Probably findbugs already does.

Dr. Josh once shot himself in the foot this way. As you're probably aware, java.util.TreeMap implements java.util.Map. Dr. Josh was foolish enough to to declare an implementation of Map.Entry in the bowels of TreeMap like so:

    static class Entry implements Map.Entry { ... }
Then a user was foolish enough to use TreeMap.Entry instead of Map.Entry in his code, and was shocked to discover that it didn't work: subsumption had been violated.

There are many lessons here, but rather than boring you with the details, we'll get right to the point: Don't reuse names unless you have a very, very good reason to do so. This includes hiding, shadowing, obscuring, and even overloading. Just because you can doesn't mean you should. If we had it to do over again, the language would be less permissive in this area. A secondary lesson is that you should always refer to a static member by the class in which it's declared (e.g., Map.Entry rather than TreeMap.Entry or, God forbid, myMapInstance.Entry).

And don't code like my brother.

Comments:

Post a Comment:
Comments are closed for this entry.