Revnic Robert-Nick
Revnic Robert-Nick

Reputation: 627

How to call overloaded method when the parameter is a subclass of a given type?

I have an abstract class A, two sub-classes called B, C and two overloaded methods who are in a separate class:

public void process(B objB) {...}
public void process(C objC) {...}

From a repository, I retrieve an entity whose type is A and I would like to make a call like this:

A objA = repo.findById(id);
process(objA);

I know a cast is required but I don't want to make a lot of checks using the instanceof or plenty of if statements.

Which are the best options to implement such a method call? And is it such a code design good?

Upvotes: 1

Views: 1570

Answers (2)

art
art

Reputation: 1332

Using instanceof is not good option especially if you have plenty of places where you need to have access to particular subclass.

  1. Obvious approach is to add abstract process() method without params into base class and implement it in subclasses by calling corresponding process(this) method from another class and passing thisreference into it.

  2. When it is preferred to divide processing logic from class hierarchy (presumably it is your case) there is a pair of patterns: Strategy or more general Visitor. Here is an example of Visitor pattern usage:

    abstract class A {
        <T> T accept(Visitor<T> visitor);
    }
    
    class B extends A {
        @Override
        public <T> T accept(Visitor<T> visitor) {
            return visitor.visit(this);
        }
    }
    
    class C extends A {
        @Override
        public <T> T accept(Visitor<T> visitor) {
            return visitor.visit(this);
        }
    }
    
    interface Visitor<T> {
        T visit(B bObject);
        T visit(C cObject);
    }
    

    Then define particular operation by implementing corresponsing visitor:

    Visitor<Integer> visitor = new Visitor<Integer>() {
        @Override
        public Integer visit(B bObject) {
            // TODO do something with bObject
            return null;
        }
    
        @Override
        public Integer visit(C cObject) {
            // TODO do something with cObject
            return null;
        }
    };
    

    and just call accept:

    A objA = repo.findById(id);
    Integer result = objA.accept(visitor);
    

Upvotes: 1

MrsNickalo
MrsNickalo

Reputation: 217

Using overloaded methods in this instance cannot be done without casting the object first since the program will not call the overloaded method if the explicit object type does not match. So it would not call process(B objB) unless the object you were passing is explicitly of class B.

In order to avoid too much hassle, there are a few ways to do it. Depending on what differentiates between B and C, you can instantiate A as that type when pulling the object from the database (this can be done in the RowMapper) and write an if else statement to determine which process to call:

        if(a.getClass() == B.class)
        {
            return process((B) a);
        }
        else
        {
            return process((C) a);
        }

Or, if you have more subclasses, you can use a switch statement. That is the simplest way I can think of.

Upvotes: 0

Related Questions