Reputation: 824
I'm using an (open-source) API in my Java project and referencing interfaces from it. One interface (Foo
) is used extensively both in the API and in projects that use it. For my project, I'd like to use an interface which only exposes a subset of the methods that Foo
does, such that Foo
would inherit methods from my new interface (Bar
). I don't want to change the interface of Foo
at all; rather, I'd like to be able to write classes which implement Bar
, and then manipulate those along with classes which implement Foo
as if they are all Bar
.
Since the API is in a different package, is there any way to accomplish this such that Bar
is a superclass (interface) of all Foo
?
Upvotes: 4
Views: 1122
Reputation: 129327
Because Java has nominal typing, such that org.theirs.Foo
is in no way related to org.yours.Foo
, even if they have the exact same method signatures, I don't think this is possible to do with inheritance in Java. Even with generics, AFAIK there is no way to say, "this method takes an instance of Bar<T extends Foo OR Bar>
".
Instead, I think you want to use an Adapter interface for the library's Foo
, with your own (i.e. a FooAdapter
, having the exact same signatures as Foo
) and have this extend Bar
. The methods you want in Bar
could then be extracted from FooAdapter
. Unfortunately your code would have to be modified so that everywhere you previously referenced their Foo
, you would instead reference either:
Bar
if the only methods called are those you would define in your interfaceFooAdapter
if methods defined in Foo
but NOT in Bar
are called.This method, although clean, and a good separation of concerns, can be painful and tedious to implement, I'm afraid.
An example of how this would work is shown in the following code snippets:
package org.theirs;
public interface Foo {
void doSomething();
void doSomethingExtra();
}
package org.mine;
public interface Bar {
void doSomething();
}
public class BarImpl implements Bar{
public void doSomething( /* implementation */ );
}
public class FooAdapter implements Bar{
private final Foo adapted;
public FooAdapter(Foo adapted) {
this.adapted = adapted;
}
public void doSomething() {
adapted.doSomething(); // delegate to adapted instance
}
}
public class UsingThoseBars {
public void doSomethingWithAllThoseBars(Collection<Bar> bars) {
// each entry in bars could either be a BarImpl or a FooAdapter
}
}
You can see from this example that the method doSomethingExtra()
is not accessible from your code, since the interface Bar
does not specify it.
Note there may be useful tricks you could do with class rewriting, such as with AspectJ. I'm assuming you'd rather have the effect you desire at compile-time, in pure Java.
Another suggestion is to have an implementation of Bar
which throws UnsupportedOperationException
for the methods of Foo
that you don't need. Although there is a precedent for this in major Java libraries (e.g. UnmodifiableList
in the JDK) I would recommend against this practice in general. However, if the cost of replacing references to their Foo
with your new FooAdapter
is quite high, it may be a good trade off to use this strategy.
Upvotes: 1
Reputation: 6515
You could use the Adapter Pattern to accomplish this:
public class FooAdapter implements Bar {
private Foo wrapped;
public FooAdapter(Foo foo) {
wrapped = foo;
}
public void op1() {
foo.op1();
}
public string op2(int param) {
return foo.op2(param);
}
}
Upvotes: 2
Reputation: 308938
I'd like to be able to write classes which implement Bar, and then manipulate those along with classes which implement Foo as if they are all Bar.
You can create an abstract Bar that implements Foo. You'll leave off the methods shared by the two and implement the methods that Bar does not share so they throw an exception like UnsupportedOperationException
.
But you can't manipulate Foo as if it were Bar; it's the other way 'round. You can't satisfy the Liskov Substitution Principle the way you're proposing.
Upvotes: 3
Reputation: 23949
You're probably going to need to use an Aspect to make this happen. It looks like AspectJ has a @DeclareParents that might do what you want.
Upvotes: 2