John
John

Reputation: 9

Java - How can a method return a variable type object without knowing the resulting type at compile time?

Let's say you have an interface and two classes which implement that interface:

public interface IElement {
    boolean isDisplayed();
}

public class Button implements IElement  {
    @Override
    public boolean isDisplayed() {
        // checks if displayed
        return false;
    }

    public void click() {
        // clicks
    }
}

public class Textbox implements IElement  {
    @Override
    public boolean isDisplayed() {
        // checks if displayed
        return false;
    }

    public void enterText(String text) {
        // enters text
    }
}

And in a completely separate class we have a method which wants to return either a Button or a Textbox depending on the value of the provided parameter. Something that like this:

public class WhichElement {

    public <T extends IElement> T getElement(String elementName) {

        if (elementName.equalsIgnoreCase("button")) {
            return new Button();
        }
        else if (elementName.equalsIgnoreCase("textbox")) {
            return new Textbox();
        }
    }
}

However that fails because the returned object isn't type T. I've tried just returning the interface like this:

public IElement getElement(String elementName)

But then I can't do what I really want to do, which is this:

WhichElement picker = new WhichElement();
picker.getElement("button").click();  <-- errors here because it's an IElement not a Button :(

I could just cast it to the correct object and trust the method to return the same one as I cast it to...but that seems dangerous. I'd much rather the method determine the type of object required and return that object directly. Is there a way to do what I'm attempting?

Upvotes: 0

Views: 128

Answers (2)

Bohemian
Bohemian

Reputation: 425013

Have two methods.

public Button getButton() {
    return new Button();
}

public Textbox getTextbox() {
    return new Textbox();
}

There's not much difference, semantically and codewise

picker.getButton()          // Strongly typed
picker.getElement("button") // Stringly typed

Plus the stringly typed version is longer.

Upvotes: 0

erickson
erickson

Reputation: 269667

You can cast the result of getElement() to T before returning, which will produce a compiler warning.

The warning is there because the compiler is going to insert a cast at the call site, and if the runtime result isn't the correct type, a ClassCastException will be thrown, from a location where there is no apparent cast. Essentially, the compiler generates the same code you'd write if getElement() was not generic, and simply returned IElement.

Instead of one factory method to produce many types, create a different factory method for each type. This will give you type safety, ensure that every element the application requires is actually provided, aid refactoring, and change runtime bugs like getElement("buttton") to compile-time errors.

Dependency injection is another approach: give the application a button instead of locating one itself.

Upvotes: 3

Related Questions