views:

289

answers:

7

I have a class structure where I would like some methods in a base class to be accessible from classes derived directly from the base class, but not classes derived from derived classes. According to the Java Language specification it is possible to override access specifications on inherited methods to make them more public, but not more private. For example, this is the gist of what I need to do, but is illegal:

// Defines myMethod
public class Base {
    protected void myMethod() {}
}

// Uses myMethod and then hides it.
public class DerivedOne extends Base {
    @Override
    private void myMethod();
}

// can't access myMethod.
public class DerivedTwo extends DerivedOne {

}

Is there any way to accomplish this?

Edited to explain why I would like to do this:

In this case the class structure is a data handling and import structure. It reads in and parses text files full of tabular data and then stores them in a database.

The base class is the base table class managing the database handling part of it. There is a fair amount of functionality contained in it that is common to all table types - as once they are in the database they become uniform.

The middle class is specific to the kind of table in the file being parsed, and has the table parsing and import logic. It needs access to some of the base class's database access functions.

The top level class is specific to the table and does nothing more than initialize the table's layout in a way the parent classes can understand. Also users of the base class do not need to see or access the database specific functions which the middle class do. In essence, I want to reveal these functions only to one level above the base class and no one else.

I ask because, although the code I posted as an example is illegal, there may be some other means to accomplish the same end. I'm asking if there is.

Perhaps hiding is the wrong way to phrase this - what I really need to do is expose some functionality that should be private to the base class to the class one level up in the hierarchy. Hiding would accomplish this - but I can see how hiding would be a problem. Is there another way to do this?

+8  A: 

No. I'm not sure why you'd quote the spec and then ask if there's any way to do the opposite of what the spec says...

Perhaps if you explain why you want to do this, you could get some suggestions on how.

Jonathan Feinberg
Okay, hiding was the wrong way to explain this. Though that would accomplish the end I need. A better way to explain this is from the other direction: I need to expose some functionality private to the base class to the class that derives directly from it - but not to classes above that.
Daniel Bingham
+5  A: 

When overriding a method you can only make it more public, not more private. I don't know why you use the word "general"

Remember that, ordering from least to most restrictive:

public<protected<default<private

Yes, "protected" is a less restrictive access modifier than default (when no modifier is used), so you can override a default method marking the overriding method as protected, but not do the opposite.

Can: You can override a protected method with a public one.

Can't: You can't override a public method with a protected one.

omgzor
You can override `protected` with `protected` in a different package, which is just weird. Ah, `protected` is "weird".
Tom Hawtin - tackline
Of course, you can always use the same access modifier to override. Alcon's asking about changing the level of access.
omgzor
Fixed word "general" ;) When my head is in code words are not my strength. I am aware of this rule - I'm asking if there's a way to work around this.
Daniel Bingham
Actually let me rephrase as I do at the end of my edit. I don't need to hide - I need to expose private functionality in a very limited way. I latched on to hiding because the most obvious way to do that at the time appeared to be making it protected then overriding with private somehow. But I can see how that would break OO. ;)
Daniel Bingham
Why did you wanna make it protected? Remember that a protected member is visible on all its package AND other packages that extend its class.
omgzor
Right - I'm originally a C++ programmer and am still more comfortable in that language despite years of Java experience at this point, and my head jumped to C++'s more private definition of protected.Basically I just want to expose it to one level of child classes and not above that or outside to the world at large...
Daniel Bingham
+4  A: 

If you did this then DerivedOne would not be a Base, from the DerivedTwo's point of view. Instead what you want is a wrapper class

//Uses myMethod but keeps it hidden
public class HiddenBase {
    private final Base base = new Base();
    private void myMethod();
    public void otherMethod() {base.otherMethod();}
}

You can't access protected methods of the base though this way...

KernelJ
Only `private final Base base = new Base();`.
Tom Hawtin - tackline
Ah yeah, thanks, I'll fix that.
KernelJ
+1  A: 

What you describe comes close to what the protected access class is for, derived classes can access, all others cannot.

If you inherit from base classes you have no control over this might pose a problem, you can make the method inaccesible to others by throwing an exception while making the inherited code available to your classes by calling super directly, something like:

// Uses myMethod and then hides it.
public class DerivedOne extends Base {
    @Override
    public void myMethod() {
        throw new IllegalStateException("Illegal access to myMethod");
    }

    private void myPrivateMethod() {
        super.myMethod();
    }

}

Edit: to answer your elaboration, if I understand you correctly you need to specify behaviour in the context of the base class which is defined in the middle class. Abstract protected methods won't be invisible to the classes deriving from the middle class.

One possible approach is to define an interface with the methods you would need to be abstract in the base class, keeping a private final reference in the base class and providing a reference to the implementation when constructing the middle class objects.

The interface would be implemented in a (static?) nested inside the middle class. What I mean looks like:

public interface Specific {
    public void doSomething();
}

public class Base {
    private final Specific specificImpl;

    protected Base(Specific s) {
        specificImpl = s;
    }

    public void doAlot() {

         // ...

         specificImpl.doSomething();

         // ...
    }
}

public class Middle extends Base {

    public Middle() {
        super(new Impl());
    }

    private Impl implements Specific {

        public void doSomething() {

            System.out.println("something done");
        }
    }
}

public class Derived extends Middle {

    // Access to doAlot()
    // No access to doSomething()
}
rsp
Hmm... that's ugly. Yeah, protected comes close, but not quite there. Good idea though. Going for just not visible. If I have to just settle on protected an instructing people to ignore them that's fine too. Was kinda hoping there was a way for a limited reveal of the functionality, just for cleanliness's sake.
Daniel Bingham
+2  A: 

Inheritance works because everywhere you can use the base class, you can also use one of it's subclasses. The behavior may be different, but the API is not. The concept is known as the Liskov substitution principle.

If you were able to restrict access to methods, the resulting class would not have the same API and you would not be able to use substitute an instance of the base class for one of the derived classes, negating the advantage of inheritance.

What you actually want to accomplish can be done with interfaces:

interface IBase1 {
}

class Derived1 implements IBase1 {
  public void myMethod() {
  }
}

class Derived2 implements IBase1 {
}

class UseMe {
  public void foo(IBase1 something) {
     // Can take both Derived1 and Derived2
     // Can not call something.myMethod()
  }
  public void foo(Derived1 something) {
     something.myMethod();
  }
  public void foo(Derived2 something) {
    // not something.myMethod()
  }
}
extraneon
Interfaces won't do this as there is functionality, not just an interface, common to all written into the base class. See the edit for a better explanation of what I'm actually trying to do.
Daniel Bingham
+1  A: 

It is possible, but requires a bit of package manipulation and may lead to a structure that is a bit more complex than you would like to work with over the long haul.

consider the following:


package a;

public class Base { void myMethod() { System.out.println("a"); } }


package a;

public class DerivedOne extends Base { @Override void myMethod() { System.out.println("b"); } }


package b;

public class DerivedTwo extends a.DerivedOne { public static void main(String... args) { myMethod(); // this does not compile... } }


I would recommend being nice to yourself, your co-workers and any other person that ends up having to maintain your code; rethink your classes and interfaces to avoid this.

vkraemer
+1  A: 

I think the very nature of the problem as you've posed it exposes conceptual problems with your object model. You are trying to describe various separate responsibilities as "is a" relationships when actually what you should be doing is describing "has a" or "uses a" relationships. The very fact that you want to hide base class functionality from a child class tells me this problem doesn't actually map onto a three-tiered inheritance tree.

It sounds like you're describing a classic ORM problem. Let's look at this again and see if we can re-map it onto other concepts than strict "is a" inheritance, because I really think your problem isn't technical, it's conceptual:

You said:

The base class is the base table class managing the database handling part of it. There is a fair amount of functionality contained in it that is common to all table types - as once they are in the database they become uniform.

This could be more clear, but it sounds like we have one class that needs to manage the DB connection and common db operations. Following Single Responsibility, I think we're done here. You don't need to extend this class, you need to hand it to a class that needs to use its functionality.

The middle class is specific to the kind of table in the file being parsed, and has the table parsing and import logic. It needs access to some of the base class's database access functions.

The "middle class" here sounds a bit like a Data Mapper. This class doesn't need to extend the previous class, it needs to own a reference to it, perhaps injected on the constructor or a setter as an interface.

The top level class is specific to the table and does nothing more than initialize the table's layout in a way the parent classes can understand. Also users of the base class do not need to see or access the database specific functions which the middle class do. In essence, I want to reveal these functions only to one level above the base class and no one else.

I'm not clear why a high-level class seems to have knowledge of the db schema (at least that's what the phrase "initialize the table's layout" suggests to me), but again, if the relationship between the first two classes were encapsulation ("has a"/"uses a") instead of inheritance ("is a"), I don't think this would be a problem.

Dave Sims
You bring up some good points, and it definitely gives me something to think about. The problem I have, is that the situation I'm dealing with doesn't lend itself to encapsulation so much. I'm afraid I can't really give more information - it's job and proprietary and I'm actually slightly worried about the amount I have given. However, I appreciate your insights and I'll take a hard second look at my structure.
Daniel Bingham
Actually now that I've looked back at my structure it would seem that my question was in fact born of monday-early-afternoon sleep-deprived-brain-fail and not enough caffeine. As it turns out the method I'd wanted to hide from the upper level classes was intended to be visible to them in case they needed to override it. I have no idea why I was thinking otherwise. In any case, good rule of thumb to keep in mind for future designs. If ever one thinks hiding is needed - one's design is wrong. Look at encapsulation. Thanks for the thoughtful and detailed answer.
Daniel Bingham