0

I have two classes implementing an interface :

public interface Vehicle {…}
public class Car implements Vehicle {…}
public class Shoes implements Vehicle {…}

At the user level I am dealing with the interface, and functions are usually of the form function(Vehicle v) :

public class Controller {

    @Inject
    Service service;

    public int get(Vehicle v) {
        return service.getShortestPathLength(v);
    }
}

However at some point I have a method which I want to be able to discriminate between the implementations, because the operations on this particular part are very different (e.g. in my example sample the paths by foot, by car, by plane or by boat would be completely different). That is, I would like getShortestPathLength(v) to switch automatically (i.e. without explicit if testing) to the correct overloaded method depending on the implementation of v :

public class Service {

    public int getShortestPathLength(Car c) {…}
    public int getShortestPathLength(Shoes s) {…}

}

However it doesn’t seem to work as is, I get an Unresolved compilation problem error :

The method getShortestPathLength(Vehicle) in the type Service is not applicable for the arguments (Car)

Is what I am trying to achieve possible, and if so what am I missing ?

My current workaround is to test v.getClass().getSimpleName() within getShortestPathLength(Vehicle v), but although it works it doesn’t seem quite an optimized use of object-oriented programming.

FWIW I’m running Java 11 with Quarkus 1.6.0.

Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
  • where do you declare foobar function? in interface I or in other classes/interfaces? – Chi Cuong Le Aug 17 '20 at 15:10
  • @ChiCuongLe `foobar` is declared in a service and called in a controller. – Skippy le Grand Gourou Aug 17 '20 at 15:13
  • is the following what you are trying: ServiceInterface -> foobar(I param), ServiceImpl1 -> foobar(A param), ServiceImpl2 -> foobar(B param)? In this case, it can not work, like the compiler complaints :D. Because that is not method overloading, that is polymorphism – Chi Cuong Le Aug 17 '20 at 15:17
  • I’m not sure, can you elaborate ? Note that the interface and classes are defined in their own directory, not in the service. – Skippy le Grand Gourou Aug 17 '20 at 15:20
  • Yes, it can, but the selection is based on _the declared (compile-time) type_. If your variable type is `I`, then runtime type is irrelevant. – chrylis -cautiouslyoptimistic- Aug 17 '20 at 15:21
  • @chrylis-cautiouslyoptimistic- So IFUC I am left with manual switch ? – Skippy le Grand Gourou Aug 17 '20 at 15:24
  • 1
    Your design is not idiomatic for Java, and if you explain your applied case in more detail we may be able to provide better advice. The general concept behind polymorphism is that `foobar` *shouldn't* care about the implementation of `I`, only the contract, and if there is some additional detail needed, then it should be part of the contract somehow. – chrylis -cautiouslyoptimistic- Aug 17 '20 at 15:34
  • I mean in my previous comment, that if ServiceInterface->foobar(I param), and you have multiple implementations for the ServiceInterface, then in these implementations, you should have same signature as that one in ServiceInterface, you can not change with A or B. That is overriding! But ServiceInterface (as well implementations) can have 2 overloading method: foobar(A param), foobar(B param). In this case, method overloading should work properly – Chi Cuong Le Aug 17 '20 at 20:16
  • @chrylis-cautiouslyoptimistic- If the design is not idiomatic then I’m probably on a wrong path. I updated my question with a more concrete (although fictive) example. – Skippy le Grand Gourou Aug 18 '20 at 10:09
  • either you have to do an `instanceof` check or use the visitor pattern. – Johannes Kuhn Aug 18 '20 at 10:10
  • @ChiCuongLe Service itself is not an interface. I updated my question with a bit more code context. – Skippy le Grand Gourou Aug 18 '20 at 10:10
  • That is not the error I get when I try to compile your code; it's "Cannot resolve method 'getShortestPathLength(Vehicle)'". Because the service doesn't have that method. – daniu Aug 18 '20 at 10:16
  • @JohannesKuhn Thanks, [`instanceof` seems better than `getSimpleName()`](https://stackoverflow.com/questions/50946488/java-instanceof-vs-class-name-switch-performance), and the visitor concept looks promising, I will look further into it. – Skippy le Grand Gourou Aug 18 '20 at 10:29
  • @daniu I don’t know why I don’t get this error, but it actually makes sense, thank you very much for mentioning it, it really helped me figuring the issue : what if I call `getShortestPathLength(v)` on a vehicle for which the function is not overloaded ? I’m probably on a wrong path indeed. – Skippy le Grand Gourou Aug 18 '20 at 10:31
  • @SkippyleGrandGourou There are two options: first one, you move getShortestPathLength() to Verhicle, and implement differently depends on individual charaters of Car and Shoes. This option increases encapsulation. If this option possible, depends on your logic. Second one: you declare 3 different functions using overloading technique in Service, and divide your code in Service implementation to small meaningful block to get them reused in different functions. Solution with check instanceof, etc in run time is not prefer in my opinion. – Chi Cuong Le Aug 18 '20 at 11:04
  • @ChiCuongLe Yes, I guess I’ll go for the first option. Please make your comment an answer so that I can accept it. – Skippy le Grand Gourou Aug 18 '20 at 12:17

1 Answers1

1

I can suggest three options:

  • Move getShortestPathLength() to Verhicle, and implement differently depends on individual charaters of Car and Shoes. This option increases encapsulation. If this option can be implemented depends on your logic. In case getShorttestPathLength can be implemented using data in Verhicle, no other information required
  • Declare 3 different functions using overloading technique in Service, and divide your code in Service implementation to small meaningful block to get them reused in different functions. By using this option, we have to give correct type of parameter for each call, in order to get correct method to be loaded.
  • Declare an interface of Service with one method: getShortesPathLength. Implement different variants for Service with different logic. Inject correctly instances of Service so that correct implementation can be called. This option based on polymorphism of Service

Your workaround is not bad, but I do not think checking type of entities in run time is good idea.

Chi Cuong Le
  • 368
  • 2
  • 7