Java - 从抽象类的子节点调用方法

时间:2015-03-18 01:31:09

标签: java abstract unbounded-wildcard

给出以下抽象类:

public abstract class BaseVersionResponse<T extends BaseVO> {

    public abstract void populate(T versionVO);

}

以及以下子类:

public class VersionResponseV1 extends BaseVersionResponse<VersionVOV1>
{
    protected String testFieldOne;
    protected String testFieldTwo;

    public String getTestFieldOne() {
        return testFieldOne;
    }  
    public void setTestFieldOne(String value) {
        this.testFieldOne = value;
    }
    public String getTestFieldTwo() {
        return testFieldTwo;
    }  
    public void setTestFieldTwo(String value) {
        this.testFieldTwo = value;
    }

    @Override
    public void populate(VersionVOV1 versionVO) {

        this.setTestFieldOne(versionVO.getFieldOne());
        this.setTestFieldTwo(versionVO.getFieldTwo());
}

我希望通过调用方法执行类似的操作:

public void getVersionInfo(String version) {

    BaseVO versionVO = null;
    BaseVersionResponse<? extends BaseVO> baseVersionResponse = null;

    baseVersionResponse = createVersionResponse(version);

    versionVO = createVersionVO(version);

    baseVersionResponse.populate(versionVO);

}

其中createVersionResponse(...)createVersionVO(...)如下所示:

public BaseVersionResponse<? extends BaseVO> createVersionResponse(String version) {

    BaseVersionResponse<? extends BaseVO> specificVersionResponse = null;

    if (version.equalsIgnoreCase("V1")) {

        specificVersionResponse = new VersionResponseV1();

    } else if (version.equalsIgnoreCase("V2"))

        specificVersionResponse = new VersionResponseV2();

    return specificVersionResponse;
}

public BaseVO createVersionVO(String version) {

    BaseVO versionVO = null;

    if (version.equalsIgnoreCase("V1")) {

        versionVO = new VersionVOV1();

    } else if (version.equalsIgnoreCase("V2"))

        versionVO = new VersionVOV2();

    return versionVO;
}

和VersionVOV1如下所示:

public class VersionVOV1 extends BaseVO {

    private String fieldOne = null;
    private String fieldTwo = null;
    private String fieldThree = null;

    public String getFieldOne() {
        return fieldOne;
    }
    public void setFieldOne(String fieldOne) {
        this.fieldOne = fieldOne;
    }
    public String getFieldTwo() {
        return fieldTwo;
    }
    public void setFieldTwo(String fieldTwo) {
        this.fieldTwo = fieldTwo;
    }
    public String getFieldThree() {
        return fieldThree;
    }
    public void setFieldThree(String fieldThree) {
        this.fieldThree = fieldThree;
    }

}

当我尝试编译这行代码时出现了问题:

baseVersionResponse.populate(versionVO);
getVersionInfo(...)中的

。我收到的消息看起来像这样:

  

BaseVersionResponse类型中填充(捕获#3-of?)的方法不适用于参数(BaseVO)

上面的populate方法。

我的想法(显然是不正确的)因为baseVersionResponse在代码中的这一点实际上是一个特定的子实例,所以该类确切地知道从该特定子类调用哪个populate方法。

我在这里做错了什么?如果这不是正确的方法,有更好的方法吗?

感谢您的时间!

2 个答案:

答案 0 :(得分:1)

如果你只是取出类型的捕获(&#34;&lt;?&gt;&#34;),并且不加以检查,它应该可以正常工作。即使使用Object类型也会编译。

但是,根据您的具体示例,您可能需要的是方法:

public BaseVersionResponse<?> createVersionResponse(String version)

更改为:

public BaseVersionResponse<? extends BaseVO> createVersionResponse(String version)

然后,而不是使用

BaseVersionResponse<?>

使用

BaseVersionResponse<? extends BaseVO>

因为您知道返回类型将是实现接口/类的那些东西之一。

答案 1 :(得分:1)

好的,我今天好好看看这个。问题在于,通配符虽然是正确的方法,但却阻止了您的操作:

BaseVO versionVO = createVersionVO(version);

因为populate调用想要一个BaseVO的扩展名,而不是一个不符合条件的实际BaseVO。这意味着您无法直接传递该versionVO变量。

所以,为了保持类型检查到位,我认为这样做很好,因为你总是想要一个实现,几乎把所有内容保留在上面,并将你的BaseVersionResponse类更改为:< / p>

public abstract class BaseVersionResponse<T extends BaseVO> {

    public T getVersion(BaseVO versionVO) {
        try {
            return (T) versionVO;
        } catch (ClassCastException e) {
            throw new IllegalArgumentException();
        }
    }

    public abstract void populate(BaseVO versionVO);

}

因此,populate方法现在需要一个BaseVO,并且有一个新的getVersion方法为我们做一些显式的转换。这应该没问题,因为我们知道工厂将始终提供正确的东西,但如果另一个调用者没有,则抛出IllegalArgumentException

现在,在响应类实现中,相应地更改populate方法:

public void populate(BaseVO version) {
    VersionVOV1 versionVO = getVersion(version);
    this.setTestFieldOne(versionVO.getFieldOne());
    this.setTestFieldTwo(versionVO.getFieldTwo());
}

所以,我们已经改变了populate方法以获取BaseVO,并且getVersion方法为我们进行了转换。所有其他类型的检查仍然适用,我们很高兴。

铸造使它感觉不那么干净,但对于你正在使用的工厂方法,它实际上是唯一的方法(我能想到)保持类型声明和代码模式的保证。< / p>

希望有所帮助!