Fog Creek Software
Discussion Board




Java: can't reduce inherited method visibility

Why is this?

If I have a protected method x in class A, and derive a class B from A, why can't I make a _private_ method x in B? Is there an alternative?


Thursday, June 24, 2004

From: http://www.sniffer.net/bookshelf_do_sniffer/java/exp/ch05_07.htm

There are two important (but unrelated) notes we need to add to the discussion of visibility with regards to class members in subclasses. First, when you override methods of a class in a subclass, it's not possible to reduce their visibility. While it is possible to take a private method of a class and override it to be public in a subclass, the reverse is not possible. This makes sense when you think about the fact that subtypes have to be usable as instances of their supertype (e.g., a Mammal is a type of Animal). If we could reduce the visibility of an overridden method, this would be a problem. However, we can reduce the visibility of a variable because it simply results in a shadowed variable. As with all shadowed variables, the two variables are distinct and can have separate visibilities in their different class forms.

The second point is that protected variables of a class are visible to its subclasses, but unlike C++, only in objects of the subclass's type or its subtypes. In other words, a subclass can see a protected variable from its superclass as an inherited variable, but it can't access the variable in a separate instance of the superclass itself. This can be confusing because often we forget that visibility modifiers don't resrtict between multiple instances of the same class in the same way that they do instances of different classes. Two instances of the same type of object can normally access all of each other's members, including private ones. Said another way: two instances of Cat can access all of each other's variables and methods (including private ones), but a Cat can't access a protected member in an instance of Animal, unless it can prove that the Animal is a Cat.

CF
Thursday, June 24, 2004

In your case: B is-an A.

That means you can pass your B-object to a method such as:


doSomething (A a) { a.x(); }


but doSomething() couldn't call method x() on your B object if reducing visibility was allowed. Increasing visibility doesn't have that effect and Java allows you to do so. Which is a cause for some confusion.

Similar thing applies to exceptions. Overriding inherited methods can declare fewer or more specific exceptions, but can't throw any exceptions that the super-class's method doesn't throw.

  -tim

a2800276
Thursday, June 24, 2004

The alternative is composition.

mike
Thursday, June 24, 2004

To flesh out a2800276's answer a bit more, making the sub-classes method more private creates a potentially impassible situation when you go to use your classes:

class ObjA {
protected int doSomething() {
    return 1;
  }
}

class ObjB extends ObjA {
private int doSomething() {
    return 2;
  }
}

class ObjC {
  public static void main(String[] args) {
    ObjA myA = new ObjB();
    System.out.println(myA.doSomething());
  }
}

The code in ObjC cannot make sense becase even though you are referring to myA as an "ObjA" it is really an "ObjB" which has a private method.

Brian
Thursday, June 24, 2004

...or to say the same thing more concretely, and show that it's not just an arbitrary technical rule, but a fundamental quality of types that could not be any other way and still make sense...

When you define the base, you describe it to its users.
For example you might say that "a Lamp is something that can be made by others to turn on and turn off."

class Lamp {
  public void turnOn() {...}
  public void turnOff() {...} }

This is not just arbitrary codey stuff.  You have just stated precisely for the record "this is what Lamps are."

If you've just told users "a Lamp can be made by others to turnOn and turnOff," you cannot then turn around and say "a DeskLamp is a Lamp and cannot be made by others to turnOff."

class DeskLamp extends Lamp {  // WRONG
  public void turnOn() {...}
  private void turnOff() {...} }

Here you've just told users, "DeskLamps can only turn themselves off.  That means that "extends Lamp" is a lie. Because "a Lamp is a thing that can be made by others to turnOff," this thing cannot be called a Lamp.  Compilers do their best to protect us from lies.

Cabby
Thursday, June 24, 2004

Hmmm.

Now I understand why the Sun Certificates have 80% failure rates on the exams.

Java is a slippery language.

dot for this one
Thursday, June 24, 2004

It's really not that hard.  If A extends B then A can be used *EVERYWHERE* B can be.  So A cannot behave in a way inconsistent with B.

There's always

protected void doSomething() {
    throw new SomeException();
}

Koz
Friday, June 25, 2004

Heh. You can write this kind of thing in C++.

But it doesn't have any effect.

class A
{
public:
    A();
    virtual ~A();

    virtual void x();

};
class B : public A
{
public:
    B();
    virtual ~B();

private:
    virtual void x();
};

int main(int argc, char* argv[])
{
    A*  p1 = new A,
    *  p2 = new B;

    p1->x();
    p2->x();

    return 0;
}

Implement the classes as you will, then compile & run. You can access B's x quite happily from "main", and the compiler makes not a sound about you getting different results than you might expect.


Friday, June 25, 2004

"Now I understand why the Sun Certificates have 80% failure rates on the exams."

What is your source for this assertion?

John Topley (www.johntopley.com)
Friday, June 25, 2004

I've used Java full-time for several years.  Does everything else in the Java language and environment make sense?  Then why do you expect this to?

You can search for years for a perfect language (I used to!), but any language will have a dozen things that frustrate you and seem to make no sense.  Just take a deep breath, accept that the language isn't perfect, and come up with some sort of hack that'll do what you want.  :-)

Java guy
Friday, June 25, 2004

This is *not* a case of a language not making sense, or being imperfect.  (Although Java is leagues away from perfect.)  This is not even a Java issue.  It is the fundamental nature of types.  What the original post suggests -- to define what some type is, and later contradict that definition in a subtype -- is what would not make sense.  Don't criticize a language for discouraging silly ideas.

Cabby
Saturday, June 26, 2004

I almost forgot...  Java Guy's "hack around it" advice is terrible.  It's no wonder our jobs are all going to India, if that's the way people work here.  Why pay BMW prices at K-Mart?

If you're needing this type violating craziness, somebody has grossly blundered the design at some level.  Figure out where the design went so horribly wrong, and fix that.

Cabby
Saturday, June 26, 2004

It doesn't matter if it makes sense or not.  It's pointless to ask why Java is the way it is.  Sun's holding on so tight no matter how much you complain and how obvious the design flaws are they aren't going to change it.

This specific case, yeah, there's a reason for it.  Next time there may not be.  But getting in the habit of asking "Why does Java do <this>?" is just going to make you frustrated.

My job's just fine, thanks, as is my code.  What the heck are you talking about, BMW and k-mart?

Java guy
Sunday, June 27, 2004

On a related topic, while reducing visibility error makes sense, I have yet to see an explanation for not allowing covariant return types in subclasses (e.g.:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4144488). If you agree, why not vote for this RFE? :)

DEBEDb
Tuesday, June 29, 2004

*  Recent Topics

*  Fog Creek Home