Java, Cannot reduce the visibility of the inherited method from object
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开发者_如何学JAVA 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?
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();
}
}
Use Composition rather than Inheritance.
i.e. class B holds a reference to a class A and internally calls methods on it.
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.
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.
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.
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
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());
I would use the adapter pattern. http://en.wikipedia.org/wiki/Adapter_pattern
精彩评论