Java - 在编译时如何在不知道结果类型的情况下返回变量类型对象?

问题描述:

比方说,你有实现该接口的接口和两个类:Java - 在编译时如何在不知道结果类型的情况下返回变量类型对象?

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 
    } 
} 

而且在一个完全独立的类我们有希望返回一个按钮或根据所提供的值的文本框的方法参数。东西是这样的:

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(); 
     } 
    } 
} 

但是失败了,因为返回的对象不是类型T. 我试着刚返回界面如下:

public IElement getElement(String elementName) 

但我不能做我真正想做的事情,这是这样的:

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

我可以将它转换为正确的对象和信任,返回相同的一个,因为我将它转换为方法......但似乎危险。我更愿意确定所需对象的类型并直接返回该对象。 有没有办法做我想做的事?

+0

你不能这样做,因为编译器不知道将''按钮''传递给'getElement'会导致'Button'被返回。如果你可以保证它是一个'Button',就投它。 –

+0

使用泛型时,如果返回'IElement',编译器会在需要进行强制转换的地方插入一个强制转换。如果你不能保证类型是正确的,你认为编译器可以如何? – erickson

+0

我建议将参数从String改为'Class clazz',然后做'clazz.newInstance()'。 – Shadov

您可以在返回之前将getElement()的结果转换为T,这会产生编译器警告。

由于编译器要在调用站点插入强制转换,并且运行时结果不是正确的类型,因此会从没有明显强制转换的位置引发ClassCastException。实质上,如果getElement()不是通用的,编译器会生成相同的代码,并且仅返回IElement

不是用一种工厂方法来生成多种类型,而是为每种类型创建不同的工厂方法。这将为您提供类型安全性,确保应用程序需要的每个元素都实际提供,帮助重构,并将运行时错误(如getElement("buttton"))更改为编译时错误。

依赖注入是另一种方法:给应用程序一个按钮,而不是定位一个本身。

+0

对T的投射将需要在getElement()内发生。即便如此,他也无法以他想要的方式调用click()。尽管如此,我还是应该使用替代方法。 – Zachary

+0

@Zachary是的,这就是我的意思,但不清楚,是吗?我会解决这个问题。 – erickson

有两种方法。

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

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

这里没有太大的区别,语义和codewise

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

加上stringly typed版本更长。