14

Continuing from this question: Why can't you reduce the visibility of a method in a Java subclass?

I need to create class B that is almost identical to class A, except that B cannot do certain things that A can.

Being a lazy programmer as I am, I tried to inherit A, only to greet with error that B cannot reduce the visibility of A methods. Duh!..

Now A is an API from a vendor, my intention is to encapsulate this API so that it is easier to use.

I wonder what is the best practice to work around this?

Community
  • 1
  • 1
Rosdi Kasim
  • 24,267
  • 23
  • 130
  • 154

8 Answers8

18

Two options:

If you need B to keep the same interface as A (so that client code can use any of the two without changes), you can override "forbidden" methods in B and have them throw an UnsupportedOperationException. For example:

public class A
{
    public int allowedMethod() { ... }
    public int forbiddenMethod() { ... }
}

public class B extends A
{
    public int forbiddenMethod()
    {
        throw new UnsupportedOperationException("Sorry, not allowed.");
    }
}

Or, if you really want the API of B to be a subset of the API of A, then just have B contain an instance of A, and delegate method calls appropriately.

    public class A
    {
        public int allowedMethod() { ... }
        public int forbiddenMethod() { ... }
    }

    public class B
    {
        private A a;

        public int allowedMethod()
        {
            return a.allowedMethod();
        }
    }
Grodriguez
  • 21,501
  • 10
  • 63
  • 107
  • The second solution was pretty much what I had in mind in the first place.. but I will have to replicate all the wanted methods manually. First solution seems 'unclean' for me.. lots of clutter and unwanted code. – Rosdi Kasim Oct 29 '10 at 03:47
  • 2
    Yes, that's true. I guess that depending on what is the ratio of methods to be kept / methods to be rejected, one of the solutions might look "cleaner" than the other one. e.g. if you only want to forbid 2-3 methods out of 20, then A probably looks cleaner. If you only want to keep 2-3 methods out of 20, then B looks cleaner. But everything else being equal I also tend to prefer B over A. – Grodriguez Oct 29 '10 at 06:01
  • 1
    Since the OP actually wanted to "reduce the visibility of (certain) inherited methods" in the first place, looks like "violating Liskov substitution" is implied in his specification, isn't it? – Grodriguez Oct 29 '10 at 10:01
9

Use Composition rather than Inheritance.

i.e. class B holds a reference to a class A and internally calls methods on it.

Community
  • 1
  • 1
William
  • 13,332
  • 13
  • 60
  • 73
  • 2
    I will have to replicate all the methods in `A` that I _do_ want to expose into `B`, and there are lots of it!. Didn't I mention I am lazy? Lol.. – Rosdi Kasim Oct 28 '10 at 09:02
  • IDEs often have a "Delegate" menu command that will write the forwarding methods in one batch. – Andrew Lazarus Apr 19 '12 at 03:43
4

A facade is used when one wants an easier or simpler interface to work with.

You would have to create your own wrapper class (Facade Pattern) around your foreign interface.

interface Foreign
{
    void dontWantThis();
    void keepThis();
}

interface/class MyForeign
{
    void keepThis();
}

The implementation would then have a instance of Foreign that it can refer calls to.

willcodejavaforfood
  • 43,223
  • 17
  • 81
  • 111
3

If B can't do all thing A can, you couldn't treat B as an A.

Maybe you need a wrapper, not a subclasse.

EDIT:

So you'd understand you'll never reduce the "visibility" of a subclass method :). Throw an exception or do nothing is not reduce the visibility, therefore you need a wrapper. Sometimes, this situation is a signal of a bad design (only sometimes).

This is very related to circle-ellipse problem.

sinuhepop
  • 20,010
  • 17
  • 72
  • 107
2

You can build a wrapper class offering a reduced API, or you can throw an exception like UnsupportedOperationException from the methods that you want to disable.

rsp
  • 23,135
  • 6
  • 55
  • 69
  • 1
    Please don't abuse `IllegalStateException`. There is no state involved here. – Grodriguez Oct 28 '10 at 09:06
  • @Grodriguez, offered another option, the actual choice of exception is up to the application model of course, and could opt for a state exception imho. – rsp Oct 28 '10 at 11:24
  • According to the documentation, the `IllegalStateException` "signals that a method has been invoked at an illegal or inappropriate time. In other words, the Java environment or Java application is not in an appropriate state for the requested operation." This is obviously misleading as the disabled methods are meant to throw the exception always, not depending on the application state or on the timing of the call. – Grodriguez Oct 28 '10 at 11:29
  • @Grodriguez, I have seen the use of `IllegalStateException` in situations where the classes represent states in a model, where the methods are called through an interface reference, making its use a valid one. As we don't know the context of the OP, I didn't want to assume his situation is different. – rsp Oct 28 '10 at 13:30
  • That would be reasonable, but it doesn't seem to be the case for the OP, since what he originally was trying to do was to *"reduce the visibility of certain methods"*. This isn't allowed in Java, but if it was allowed, obviously it would not depend on any application state. Edit: I see you already changed that to `UnsupportedOperationException`. – Grodriguez Oct 28 '10 at 14:58
1

Another option you might consider.

Say you want to reduce the API of some class:

public class LargeApi {
  public void doFoo() { ... }
  public void doBar() { ... }
  public void doBaz() { ... }
  ...
  ...
}

Such that clients would only be exposed to say the doFoo method (or any method you'd prefer them to use instead):

public interface ReducedApi {
  void doFoo();
}

But in order for instances of the ReducedApi to be used anywhere the LargeApi is expected, you need a way to get back to the LargeApi (preferably without casting):

public interface ReducedApi {
  void doFoo();
  LargeApiClass asLargeApiClass();
}

An example implementation forcing clients to use your new reduced API might look like the following:

public class ReducedApiImpl 
  extends LargeApi 
  implements ReducedApi {

  // Don't let them instantiate directly, force them to use the factory method
  private ReducedApiImpl() {

  }

  // Notice, we're returning the ReducedApi interface
  public static ReducedApi newInstance() {
    return new ReducedApiImpl();
  }

  @Override
  public void doFoo() {
    super.doFoo();
  }

  @Override
  public LargeApi asLargeApi() {
    return this;
  }
}

Now your clients can use your reduced api for the common case, but cast back to the large api when needed:

ReducedApi instance = ReducedApiImpl.newInstance();
instance.doFoo();
callSomeMethodExpectingLargeApi(instance.asLargeApi());
Matthew Madson
  • 1,643
  • 13
  • 24
1

The solution would probably use "composition" over "inhertence". You could have a property in class B, of type A. Then expose only those methods in B that you want to actually implement

madhurtanwani
  • 1,199
  • 7
  • 13
0

I would use the adapter pattern. http://en.wikipedia.org/wiki/Adapter_pattern

punkers
  • 107
  • 2