Reputation: 711
I have two Java classes B and C in which both extend from class A (I do not own class A).
public class B extends A {…}
public class C extends A {…}
I need a list of type „ListItem“ to hold instances of either B or C, but since both (B and C) are already extended, they cannot be further extended by using „ListItem“ as a superclass.
I think the only way to go is with composition („has-a“ relation…).
Since „ListItem“ should never have B AND C (but only one of them), I am planning to create ListItem with 2 constructors and set an appropriated inner type to reflect if either B or C is being hold.
Is there a better approach to accomplish this? Please see the pseudo code below.
public class ListItem {
private enum elemType {
TYPE_B, TYPE_C
}
private final B mBItem;
private final C mCItem;
private final elemType mType;
// used to hold instance of B
public ListItem(B b) {
this.mBItem = b;
this.mType = TYPE_B;
}
// used to hold instance of C
public ListItem(C c) {
this.mCItem = c;
this.mType = TYPE_C;
}
// sample method to demonstrate delegation
private int getAge() {
int age;
if (containsB()) {
age = mBItem.getAge();
}
else if (containsC()) {
age = mCItem.getAge();
}
else {…}
return age;
}
private boolean containsB() {
return (mType == TYPE_B);
}
private boolean containsC() {
return (mType == TYPE_C);
}
}
Upvotes: 1
Views: 120
Reputation: 40406
I mean, you have:
public class B extends A {…}
public class C extends A {…}
So you could just make your item hold an A
:
public class ListItem {
private final A item;
public ListItem (A item) {
this.item = item;
}
}
And you have any number of options on how to handle it from there, e.g. simply:
public A getItem () {
return item;
}
And do any appropriate casts or tests from the caller. That's the most flexible way. Or I suppose something like:
public B getB () {
return item instanceof B ? (B)item : null;
}
public C getC () {
return item instanceof C ? (C)item : null;
}
But that's starting to get sloppy, and limits you from storing any other A
-derived stuff in your ListItem
.
You might want to start questioning your design here. For example, if you're adding some functionality to A
that is common to B
and C
and necessary for the proper operation of a ListItem
, consider deriving some class from A
to add the common stuff, derive B
and C
from that, and make your ListItem
store that base (of course, if you do this you can't store an A
any more):
public class AgedA extends A {
...
public int getAge () { return ... }
}
// Then your B and C both extend AgedA, and your ListItem stores an
// AgedA and can use getAge().
You could make AgedA
be abstract and getAge()
be virtual, if that's more appropriate. Or you could declare an interface that defines getAge()
and use that.
Yet another option is you could (as pointed out in comments) make ListItem
be either an interface, or a class that extends A
, then have your B
and C
implement or extend that.
I suppose you could also use generics for ListItem
e.g. <? extends A>
, although that may not be appropriate especially if your ListItem
s are mixed. The major benefit of using generics is that e.g. getters can now directly return the subtypes without casts and you give yourself compile-time ability to restrict types to a specific subtype in function parameters and stuff, but otherwise it's essentially the same as just storing an A
.
In any case, aside from the advice to just store A
in ListItem
and handle different types at a higher level, I can't in good conscience give you any other approach suggestions, because the need for all of this seems questionable to me.
Upvotes: 3
Reputation: 17071
I think a clean approach to this is to have your ListItem
be an abstract class extending from A
, then have B
and C
extend from ListItem
.
Why make ListItem
abstract? Any behavior that is shared across B
and C
can be implemented in the abstract class, and you can additionally impose interfaces requirements on its subclasses.
public abstract class ListItem extends A {
public int getAge () {
// Both B and C share this implementation
}
// But B and C behave differently for this method
public abstract void manipulate ();
}
public class B extends ListItem {
public void manipulate () {
// Something specific to B here.
}
}
public class C extends ListItem {
public void manipulate () {
// Something specific to C here.
}
}
Then you can declare your list of ListItem
s, insert instances of B
and C
as necessary, and manipulate them according to the concrete methods or the abstract interface in ListItem
:
ArrayList<ListItem> list = new ArrayList<>();
list.add(new B());
list.add(new C());
System.out.println(list.get(0).getAge());
list.get(1).manipulate();
Upvotes: 3
Reputation: 3986
From your description it looks like ListItem
is a container for items of type A (B or C, other sub classes in the future). I would recommend using generics, using <? extends A>
, you wouldn't then need the more than one constructor and all of A's methods should be available.
Upvotes: 0