36

In my abstract class, I have something like this:

public Object methodIWantToExpose(){
  // ... 
  methodIDontWantExposed()
  // ...
}

protected abstract void methodIDontWantExposed();

The thing is, I want to force the person that extends methodIDontWantExposed() to make it protected, because I don't want the extending class to have both methodIDontWantExposed and methodIWantToExpose exposed.

Is there a way to do this (or a different approach which might avoid my problem)?

justsomeusername
  • 495
  • 4
  • 11
  • You want to make sure any concrete subclass doesn't make methodIDontWantExposed public? – aioobe Feb 23 '15 at 10:27
  • A subclass can export any behavior whatsoever that is accessible to it from a superclass. The access level of the method holding that behavior is irrelevant. – Marko Topolnik Feb 23 '15 at 10:33
  • 72
    Like in the real world, there's only so much parents can do to stop their children doing something stupid. – biziclop Feb 23 '15 at 10:54
  • The solution to this in C++ is the use of protected abstract nested classes, which are used by the containing class. It seems like Java supports a similar construct. – Eric Feb 23 '15 at 19:04
  • 1
    I'm afraid that what you're trying is a symptome of something you'd be better off to avoid. So maybe there is a better design solution to your problem available. If you'd share what you actually want to achieve you might get a better answer than the simple "No" you can get here. – David Triebe Feb 23 '15 at 19:04
  • Why would you want to do that? You are assuming that this method will never have a viable meaning in a public context in a derived class. What justifies that assumption? What you don't want is someone making your `public abstract void methodIWantToExpose` protected or private. C++ lets you do that. Java does not, thereby protecting against this kind of violation of the Liskov substitution principle. – David Hammen Feb 23 '15 at 20:47
  • 6
    Imagine if this was possible. Then what stops a subclass from adding `public void callMethodIDontWantExposed() {methodIDontWantExposed();}` anyway? – user253751 Feb 24 '15 at 07:00
  • possible duplicate of [Why Java allows increasing the visibility of protected methods in child class?](http://stackoverflow.com/questions/12780779/why-java-allows-increasing-the-visibility-of-protected-methods-in-child-class) – Raedwald Feb 25 '15 at 07:59

6 Answers6

38

No. A subclass can always make a method more public.

Even if they couldn't do this with the method you have in your class, they could always write:

public void callsMethodIDontWantExposed() {
    methodIDontWantExposed();
}

... so you'd be in the same situation.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    I'm not against the user of my abstract class exposing the method if he (for some strange reason) *really* wants to do it. I just need a way to make it easier for him to make it protected than to make it public. In other words, I want people to make it protected unless they really know what they're doing, because making it public is *probably* not what they need. – justsomeusername Feb 23 '15 at 10:53
  • 12
    I would suggest that you probably just want documentation to that effect then. – Jon Skeet Feb 23 '15 at 10:54
  • Ok, I think I'll do that. – justsomeusername Feb 23 '15 at 11:01
  • 2
    @justsomeusername It sounds to me as if you're trying to guard against someone else making a careless or ignorant mistake. In my experience overriding methods is done to indtroduce a polymorphic change, very rarely do I even consider increasing visibility. In this case I cannot see documentation being beneficial. Do you really want to waste effort documenting half your protected methods with: "_If you override this, don't make it public unless you need it public._" ? No amount of effort on your part will guarantee people won't make mistakes using/inheriting from your class. – Disillusioned Feb 23 '15 at 16:05
  • 2
    It's interesting to note a difference between Java and C# here: in C# a method which takes a parameter of a type that is not publicly available, or returns something of such a type, cannot be publicly exposed; Java has no such restriction, and will even allow such methods to be called from outside code. – supercat Feb 23 '15 at 19:16
  • 1
    @supercat: Indeed. It's present in type hierarchies too - in C# you can't derive a public type from an internal type, whereas Java let's do the equivalent, such that you can't necessarily "see" all the inherited members, even if they're public. Personally I find the C# approach a bit more consistent. – Jon Skeet Feb 23 '15 at 19:18
  • @JonSkeet: Arguably more consistent, but it precludes some useful semantics. For example, it would allow an assembly to expose an interface and promise to code receiving that type (whether inside or outside the assembly) that it would only receive implementations defined in the assembly. It would also allow classes to define "dummy" hierarchy layers in cases where semantics require it (e.g. overriding a base-class method while shadowing it with one that has a more-specific return type) without having to expose such types to the outside world. – supercat Feb 23 '15 at 19:25
  • @supercat: Yup, I've wanted things a bit like that before. I've worked around the first using public abstract classes where one of the abstract methods is internal. No external code can derive from it :) – Jon Skeet Feb 23 '15 at 19:26
  • @JonSkeet: I think that works if all of the types can inherit from a common base; that's a common scenario, but doesn't always apply. It will also require any derived types with a public constructor to be sealed, making it impossible to sub-derive from them should the need arise (there's no way to unseal the types without allowing outside derivation). – supercat Feb 23 '15 at 20:03
34

No, a subclass can always widen the access when overriding a method. There's no way to prevent that. Usually however, when I override a method I rarely change visibility from protected to public. Documenting the purpose of the method carefully might be enough to convince the implementer that it would be a bad idea in this case.

If you really want to encapsulate the behavior in a private way, you could do something along the following lines:

abstract class YourClass {
    private HandlerInterface unexposedHandler;

    YourClass(HandlerInterface handler) {
        unexposedHandler = handler;
    }

    public Object methodIWantToExpose(){
        // ... 
        handler.methodIDontWantExposed();
        // ...
    }
}

With Java 8 you could even make HandlerInterface a functional interface and conveniently use a lambda as follows:

class YourSubClass extends YourClass {
    YourSubClass() {
        super(() -> {
            System.out.println("This is the unexposed code");
        });
    }

    ...
}
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • This isn't worth it in my situation (I'll just put a warning into the documentation), but I'll accept this answer because it gives an actual workaround. – justsomeusername Feb 23 '15 at 12:19
  • Right. I agree that proper documentation is probably (and unfortunately) the best way in this case. – aioobe Feb 23 '15 at 12:25
  • But the subclass could still store the reference by overriding that constructor and then provide access if desired right? – juunas Feb 24 '15 at 07:18
  • Well of course. In fact any subclass *has* to "override" that constructor and call it explicitly. – aioobe Feb 24 '15 at 08:16
6

It depends on whether you want users of your class to be able to override that method or not. If you let them override it, they can always override it with a public method.

Some ways you can avoid letting them override that method :

  1. Have it implemented in the abstract class and make it final, so it can't be overridden.

  2. Make it package private and implement it in one of more sub-classes that belong to the same package. Since you'd be the one implementing it, you can keep it package private, or change it to final protected, so that sub-classes outside your package wouldn't be able to override it with a public method.

Eran
  • 387,369
  • 54
  • 702
  • 768
6

In certain cases you can avoid having an abstract method (and indeed even subclassing) altogether and instead you can use a strategy defined in an enum.

Of course the enum instance used can still be exposed but at least it's guaranteed that the only behaviours allowed are the ones defined in your enum.

So your solution would be something like this:

enum Behaviour {
  ONE() {
     public Object doSomething() { return "ONE"; }
  };

   public abstract Object doSomething();
}

// and your class

public abstract class MyClass {
   private final Behaviour behaviour;

   protected MyClass( Behaviour behaviour ) {
     this.behaviour = behaviour;
   }

   public final void methodIWantToExpose() {
      // ...
      behaviour.doSomething();
      // ...
   } 
}
biziclop
  • 48,926
  • 12
  • 77
  • 104
5

You can't because increasing the visibility of a method does not break base class contract.

Take a look at Why Java allows increasing the visibility of protected methods in child class?

Community
  • 1
  • 1
antonio
  • 18,044
  • 4
  • 45
  • 61
  • It may not break the general "base-class" contract, but a base class may perfectly legitimately define a contract which would forbid such exposure (in which case such visibility would violate that contract). Such a thing is allowed in the language because it is not designed to enforce many kinds of contracts on behalf of base classes. – supercat Feb 23 '15 at 19:05
4

No you can't. If it were to be a concrete method, you could make it private. In your case there is no smart way to prevent an extending class from declaring it public (you can of course comment the method, saying it shouldn't be made public).

Kayaman
  • 72,141
  • 5
  • 83
  • 121