Reputation: 145
Assume we have a Parent interface with compare() function.
public interface Parent {
public int compare(Parent otherParent);
}
Suppose that children Child1, Child2, Child3 implement this interface Parent
public class Child1 implements Parent {
@Override
public int compare(Parent other) {
Child1 otherChild = (Child1)other;
}
}
Also, I am using generics <T extends Parent>
everywhere else in the code. So I need to compare two objects of type T from other parts of code.
I understand this is a bad design as I am typecasting the Parent object in compare() function and I don't even know whether the input is of type Child1.
How can I avoid this type casting while using generics?
Upvotes: 8
Views: 1407
Reputation: 54639
The pattern that you described there is exactly the pattern of Comparable
. In fact, you should consider omitting your Parent
interface, and replace it with Comparable
instead.
The Comparable
interface and its uses also show how this can be solved: The type can be given as a parameter, to make sure that only the matching type can be passed to the compare
method:
interface Parent<T> {
int compare(T that);
}
class Child1 implements Parent<Child1> {
@Override
public int compare(Child1 that) {
...
}
}
For all other cases, you would at least have to think about what should happen when different Child
-classes are compared to each other:
Child1 c1 = new Child1();
Child2 c2 = new Child2();
// Should this be possible? What should happen here?
// How should different child classes be compared?
c1.compare(c2);
Upvotes: 2
Reputation: 377
Why not this?
interface Parent<T extends Parent<?>> {
int compare(T type);
}
class Child1 implements Parent<Child1>{
@Override
public int compare(Child1 type) {
return 0;
}
}
Edit: To insure correct usage you can use
interface Parent<T extends Parent<T>>{ /* ... */ } //instead of wildcard
But to be honest that "loop" doesn't look pretty, and since Generics in Java don't work at RunTime (more information), they're essentially syntactic-sugar for that same cast you called "bad design" so I don't think your current approach is bad.
Upvotes: 6
Reputation: 15673
You can't avoid it in the general case. However, if the number of child classes is fixed, you can apply something akin to the visitor pattern to avoid casting at the expense of writing more code:
interface Parent {
int compare(Parent p);
protected int compareWith(Child1 c);
protected int compareWith(Child2 c);
protected int compareWith(Child3 c);
}
class Child1 implements Parent {
@Override int compare(Parent p) {
return p.compareWith(this);
}
@Override int compareWith(Child1 c) {
//specific code to child1
}
@Override int compareWith(Child2 c) {
//specific code to child2
}
@Override int compareWith(Child3 c) {
//specific code to child3
}
}
// and so on for the other children
This does avoid casting, but I'm not sure the extra effort is worth it in the case you presented here.
Upvotes: 2
Reputation: 726539
The answer depends on how you treat your inheritance hierarchy:
instanceof
Here is a sample implementation using an abstract class:
// Use this class as the base class of your Child classes
class AbstractChild implements Parent {
@Override
public int compare(Parent otherObj) {
if (!()) {
throw new IllegalStateException("Unexpected implementation of Child");
}
AbstractChild other = (AbstractChild)otherObj;
return doCompare(other);
}
protected abstract int doCompare(AbstractChild other);
protected abstract int accept(Child1 c1);
protected abstract int accept(Child2 c2);
protected abstract int accept(Child3 c3);
}
class Child1 extends AbstractChild {
@Override
protected int doCompare(AbstractChild other) {
return other.accept(this);
}
@Override
protected int accept(Child1 c1) {
// Compare c1 instance to this object
}
@Override
protected int accept(Child2 c2) {
// Compare c2 instance to this object
}
@Override
protected int accept(Child3 c3) {
// Compare c3 instance to this object
}
}
class Child2 extends AbstractChild {
@Override
protected int doCompare(AbstractChild other) {
return other.accept(this);
}
@Override
protected int accept(Child1 c1) {
// Compare c1 instance to this object
}
@Override
protected int accept(Child2 c2) {
// Compare c2 instance to this object
}
@Override
protected int accept(Child3 c3) {
// Compare c3 instance to this object
}
}
class Child3 extends AbstractChild {
@Override
protected int doCompare(AbstractChild other) {
return other.accept(this);
}
@Override
protected int accept(Child1 c1) {
// Compare c1 instance to this object
}
@Override
protected int accept(Child2 c2) {
// Compare c2 instance to this object
}
@Override
protected int accept(Child3 c3) {
// Compare c3 instance to this object
}
}
The only cast in this approach is at the abstract class level. The implementations of the actual comparison logic are contained in accept
methods, where the comparison is done between two objects of known type.
Upvotes: 3
Reputation: 36347
You can't. Java Interfaces are exactly what the name implies – an interface dictating the types accepted; so there needs to be exactly the same method signature at compile time as defined in the interface.
Yes, casting this back to a Child type feels like bad design – but it's really the bad design that Java dictates, so that's not your fault.
Upvotes: 2