eRedekopp
eRedekopp

Reputation: 63

How to get overloaded method to recognize specific subclass of generic type?

I have a method which is often called with an argument of generic type, and I want it to behave differently depending on the specific subclass of the argument. For example, the code below:

public class Foo {

    public static void fooMethod(Foo f) {
        System.out.println("fooMethod Foo");
    }

    public static void fooMethod(Bar b) {
        System.out.println("fooMethod Bar");
    }

    public static void main (String[] args) {

        Foo f = new Foo();
        Bar b = new Bar();

        Baz<Bar> bz1 = new Baz<>();
        bz1.setT(b);
        Baz<Foo> bz2 = new Baz<>();
        bz2.setT(f);

        System.out.println("calling fooMethod with foo");
        fooMethod(f);
        System.out.println("calling fooMethod with bar");
        fooMethod(b);
        System.out.println("calling bazMethod with foo");
        bz2.bazMethod();
        System.out.println("calling bazMethod with bar");
        bz1.bazMethod();
    }

    private static class Bar extends Foo {

    }

    private static class Baz<T extends Foo> {

        private T t;

        public void setT(T t) {
            this.t = t;
        }

        public void bazMethod() {
            Foo.fooMethod(this.t);
        }
    }
}

results in the following output:

calling fooMethod with foo
fooMethod Foo
calling fooMethod with bar
fooMethod Bar
calling bazMethod with foo
fooMethod Foo
calling bazMethod with bar
fooMethod Foo

I would have expected it to call the bar method in the 4th line since T is bar in that case, but it always recognizes it as the parent class and gives the default behavior.

I've thought about making a bunch of calls to instanceOf in the method, but that feels kind of hacky. The other solution I've thought of is to have an abstract method overridden in each subclass that calls the correct method, but this also seems really hacky. Is there a correct way to do this? Or if I'm approaching this all wrong, is there a better way to handle this situation?

Upvotes: 1

Views: 65

Answers (1)

Mihir Kekkar
Mihir Kekkar

Reputation: 528

The problem you are dealing with here is with Type Erasure. You can read about it in detail on the Oracle website.

Essentially, your Baz objects must treat all inputs as Foo variables because it does not know about any additional functionality that the input may have. To fix this you can make Foo and Bar have their own non-static versions of fooMethod() so that Bar's fooMethod() overrides the Foo class fooMethod()

public class Foo {

    public void fooMethod() {
        System.out.println("fooMethod Foo");
    }

    public static void main (String[] args) {

        Foo f = new Foo();
        Bar b = new Bar();

        Baz<Bar> bz1 = new Baz<>();
        bz1.setT(b);
        Baz<Foo> bz2 = new Baz<>();
        bz2.setT(f);

        System.out.println("calling fooMethod with foo");
        f.fooMethod();
        System.out.println("calling fooMethod with bar");
        b.fooMethod();
        System.out.println("calling bazMethod with foo");
        bz2.bazMethod();
        System.out.println("calling bazMethod with bar");
        bz1.bazMethod();
    }

    private static class Bar extends Foo {

        public void fooMethod() {
            System.out.println("fooMethod Bar");
        }
    }

    private static class Baz<T extends Foo> {

        private T t;

        public void setT(T t) {
            this.t = t;
        }

        public void bazMethod() {
            t.fooMethod();
        }
    }
}

The output from running this:

calling fooMethod with foo
fooMethod Foo
calling fooMethod with bar
fooMethod Bar
calling bazMethod with foo
fooMethod Foo
calling bazMethod with bar
fooMethod Bar

Upvotes: 2

Related Questions