JJ123
JJ123

Reputation: 644

How to use generics in Java when dealing with multiples heterogeneous objects?

Let's say, I have two classes that look like this:

public class classA {
    private Boolean is_started;

    public classA(Boolean is_started) {
        this.is_started = started;
    }
    public Boolean getIs_started(){
        return this.is_started;
    }
}

public class classB {
    private String token;

    public classA(String token) {
        this.token = token;
    }
    public String get_token(){
        return this.token;
    }
}

I am calling those two classes from another class like this:

public class CallingClass {

    public void function1() {
        ClassA stepA = new ClassA(<some boolean>);
        commonFunction(stepA);
}
    public void function2() {
        ClassB stepB = new ClassB(<some string>);
        commonFunction(stepB);
}
    public <T> void commonFunction(Class<T> dataObject) {
        //An if statement that has a condition that only calls a function in classB {
            String token = dataObject.get_token();
        }
        //An if statement that has a condition that only calls a function in classA {
        Boolean is_started = dataObject.getIS_started();
        }
//It returns error : The method [get_token()/getIS_started()] is undefined for the type Class<T>

I want to make sure that I can call different objects without specifying them in the function. For example, I want to supply ClassA and ClassB as an argument to the commonFunction as in the example above. How do I make it happen with generics?

Upvotes: 0

Views: 74

Answers (3)

user102008
user102008

Reputation: 31323

The direct answer to your literal question would be that the only supertype of classA and classB is Object, so your parameter type must be Object, and you can use instanceof to test if it's an instance of a particular type, and if so, cast to that type to do stuff that can only be done with that type:

public void commonFunction(Object dataObject) {
    if (dataObject instanceof classB) {
        String token = ((classB)dataObject).get_token();
    }
    if (dataObject instanceof classA) {
        Boolean is_started = ((classA)dataObject).getIS_started();
    }
}

However, using instanceof like this is evidence of bad design. If the logic for the two different types is separate, then why put them in the same function? If the "token" and "is_started" are supposed to represent the same kind of information, then refactoring them to the same method provided in an interface, like other answers suggested, makes more sense.

Upvotes: 1

Dave S
Dave S

Reputation: 609

It could be that there's something I'm missing about your question because it's not entirely clear what you are trying to achieve but something doesn't seem quite right about the way you're trying to do this.

You have logic in commonFunction that behaves one way if the argument is one type and another way if the argument is the other type. It seems like what you really should do is have that logic inside ClassA and ClassB. So they should both implement some method that behaves one way inside ClassA and the other way inside ClassB. Then commonFunction can treat all arguments the same.

In general, if you are testing the type of an argument and triggering different behaviour in different cases then it's a pretty good indication that the behaviour really belongs inside those classes themselves, provided you are the one defining the classes which you are in this case.

Upvotes: 0

MadProgrammer
MadProgrammer

Reputation: 347314

You need some way to tie the classes together which says "this instance implements a method called getToken which returns some kind of result"

The simplest place to start would be a interface, for example...

public interface Tokenable<T> {
    public T getToken();
}

Then both ClassA and ClassB would need to implement this interface as required, for example...

public class ClassA implements Tokenable<Boolean> {

    private Boolean started;

    public ClassA(Boolean started) {
        this.started = started;
    }

    @Override
    public Boolean getToken() {
        return started;
    }

}

public class ClassB implements Tokenable<String> {

    private String token;

    public ClassB(String token) {
        this.token = token;
    }

    @Override
    public String getToken() {
        return token;
    }

}

And then you can communalise the call back...

public class CallingClass {

    public void function1() {
        ClassA stepA = new ClassA(false);
        commonFunction(stepA);
    }

    public void function2() {
        ClassB stepB = new ClassB("hello");
        commonFunction(stepB);
    }

    public <T> void commonFunction(Tokenable<T> dataObject) {
        T token = dataObject.getToken();
    }

}

Without providing some kind of common ancestor, you're not really going to be able to get it to work - Class doesn't define a getToken method, so you need some kind of common ancestor in order to call it (there is a "hacky" way to do it, but you really, really, really don't want to go down that rabbit hole)

Upvotes: 4

Related Questions