Java Polymorphism: how can I avoid inputting input parameters?

Suppose we have a parent interface with the compare () function.

public interface Parent { public int compare(Parent otherParent); } 

Suppose that children Child1, Child2, Child3 implement this Parent interface

 public class Child1 implements Parent { @Override public int compare(Parent other) { Child1 otherChild = (Child1)other; } } 

In addition, I use generics <T extends Parent> everywhere in the code. So I need to compare two objects of type T with other parts of the code.

I understand that this is a bad design, because I am typing the Parent object in the compare () function, and I don’t even know if the input is of type Child1.

How to avoid this type of exception when using generics?

+7
java generics polymorphism casting
source share
5 answers

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 ensure proper use, you can use

 interface Parent<T extends Parent<T>>{ /* ... */ } //instead of wildcard 

But to be honest, the “loop” doesn’t look very nice, and since Generics in Java do not work in RunTime ( more info ), they are essentially syntactic sugar for the same cast that you called “bad design”, so I don’t I think your current approach is bad.

+5
source share

The answer depends on how you feel about your inheritance hierarchy:

  • If derived classes should only compare with instances of only their own class, and they are allowed to throw an error when representing an object of another class, use actuation, but protect it with instanceof validation
  • If derived classes must be compared across type boundaries, a more complex strategy is required to avoid casting: you can use a visitor-like template to implement the double dispatch needed to perform the comparison.

Here is an example 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 emphasis in this approach is at the level of the abstract class. Implementations of the actual comparison logic are contained in the accept methods, where a comparison is performed between two objects of a known type.

+3
source share

You can not. Java interfaces - this is exactly what the name implies - an interface that dictates the accepted types; therefore, at the time of compilation, the method signature must be exactly the same as defined in the interface.

Yes, casting it back to a Child type seems like a bad design, but it's really a bad design that Java dictates, so this is not your fault.

+2
source share

You cannot avoid this in the general case. However, if the number of child classes is fixed, you can apply something similar to the visitor pattern to avoid casting by 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 avoids casting, but I'm not sure the extra effort is worth what you presented here.

+2
source share

The template that you described has exactly the Comparable template. In fact, you should abandon the Parent interface and replace it with Comparable .

The Comparable interface and its use also show how this can be solved: the type can be specified as a parameter to ensure that only the matching method 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 should at least think about what should happen when different Child classes are compared with 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); 
+2
source share

All Articles