0

If I have multiple implementations of a method, one of which takes an instance of a particular class, the other a superclass of it, how can I force the most specific one to be called?

Let us take the following code:

public static class Cat {};
public static class DomesticCat extends Cat {};
public static class Lion extends Cat {};

public static class CatOwner {
    public Cat cat;
}

public static void identifyCat(Cat cat) {
    System.out.println("Generic cat");
}

public static void identifyCat(Lion lion) {
    System.out.println("Lion");
}

Now if I do:

identifyCat(new Cat());
identifyCat(new Lion());
identifyCat(new DomesticCat());

I get:

Generic cat
Lion
Generic cat

So far, so good, Java picks the most specific one—except that matching is done based on the declared class (or whatever I cast it to), not the actual one. For example, the following code:

Cat simba = new Lion();
identifyCat(simba);

calls identifyCat(Cat), not identifyCat(Lion)—because simba is declared as Cat.

For a more realistic use case, let us assume that Cat and all its subclasses are defined in an external library (which I have no control over), and I get a Cat instance from there with no control over the actual class of the instance, like:

Cat cat = CatFactory.getCat();
identifyCat(cat);

This will always call identifyCat(Cat), even if the Cat instance returned is of type Lion.

Considering I might have more implementations for identifyCat(), and they do more than just identify the instance (they might, for instance, interact with members introduced by that particular subclass): is there an easy way to get my code to call identifyCat(Lion) for a Lion (even if declared as Cat), without resorting to enumerative if statements à la if (cat instanceof Lion) identifyCat((Lion) cat)?

user149408
  • 5,385
  • 4
  • 33
  • 69
  • 3
    [Double Dispatch](https://en.wikipedia.org/wiki/Double_dispatch) to the rescue. – Turing85 Sep 07 '19 at 19:17
  • Actually, I think your code violates the [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). If for your business rules, it is vital to distinguish between `Cat` and `Lion`, then you should not have a `designCat(Cat cat)` as you have it now. – Turing85 Sep 07 '19 at 19:26

2 Answers2

0

If you don't want to use instanceof operator with a switch case.

Since method overloading happens at compile-time, but what you want to achieve is a dynamic method call resolution, You will need to use reflection.

import java.beans.Statement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Cat {
}

class DomesticCat extends Cat {
}

class Lion extends Cat {
}

class CatOwner {
    public Cat cat;
}

public class Abc {



    public  void identifyCat(Cat cat) {
        System.out.println("Generic cat");
    }

    public  void identifyCat(Lion lion) {
        System.out.println("Lion");
    }
    public  void identifyCat(Object lion) {
        System.out.println("object");
    }

    public void invokeSpecific(Object object,String methodName) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {


        Statement s = new Statement(this, methodName, new Object[] { object });
        Method findMethod = s.getClass().getDeclaredMethod("getMethod", Class.class,
                                                           String.class, Class[].class);
        findMethod.setAccessible(true);
        Method mostSpecificMethod = (Method) findMethod.invoke(null, this.getClass(),
                methodName, new Class[] { object.getClass() });
        mostSpecificMethod.invoke(this, object);
    }


    public static void main(String[] args) throws  NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Abc a = new Abc();  
        Object simba = new Lion();
        a.invokeSpecific(simba,"identifyCat");
    }

}

You can also find more similar ways here Finding most specific overloaded method using MethodHandle

-2

Overloaded methods are statically bound where are overridden methods are bound dynamically. I would recommend creating a getType method in each of your classes to return the type and use that in the static method to print.

A refresher - Here are a few important differences between static and dynamic binding:

  • Static binding in Java occurs during compile time while dynamic binding occurs during runtime.
  • private, final and static methods and variables use static binding and are bonded by compiler while virtual methods are bonded during runtime based upon runtime object.
  • Static binding uses Type (class in Java) information for binding while dynamic binding uses object to resolve binding.
  • Overloaded methods are bonded using static binding while overridden methods are bonded using dynamic binding at runtime.
Rohit Shetty
  • 341
  • 2
  • 10
  • The thing is, `Cat` and its subclasses are not my classes but taken from a library, no way to add code to them. Also, the real code is about more than just printing the class name, it is about logic involving specific class members—identifying the class is just a way of illustrating how different code gets executed for different classes. – user149408 Sep 07 '19 at 19:36