0

Given the following abstract class:

public abstract class BaseVersionResponse<T extends BaseVO> {

    public abstract void populate(T versionVO);

}

and the following child class:

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

I desire to do something like this from a calling method:

public void getVersionInfo(String version) {

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

    baseVersionResponse = createVersionResponse(version);

    versionVO = createVersionVO(version);

    baseVersionResponse.populate(versionVO);

}

where createVersionResponse(...) and createVersionVO(...) look like this:

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

and VersionVOV1 looks like this:

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

}

My problem arises when I try to compile this line of code:

baseVersionResponse.populate(versionVO);

in getVersionInfo(...). I'm getting a message that looks like this:

The method populate(capture#3-of ?) in the type BaseVersionResponse is not applicable for the arguments (BaseVO)

on the populate method above.

My thought was (which is apparently incorrect) that since the baseVersionResponse is, at this point in the code, actually a specific child instance, that the class would know exactly which populate method to call from that specific child class.

What am I doing wrong here? Is there a better way to do this if this isn't the correct approach?

Thank you for your time!

risingTide
  • 1,754
  • 7
  • 31
  • 60

2 Answers2

1

If you just take out the capture of type (the "<?>"), and leave it unchecked, it should work just fine. Even using type Object would have compiled.

But, given your specific example, what you probably want is the method:

public BaseVersionResponse<?> createVersionResponse(String version)

Changed to:

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

Then, instead of using

BaseVersionResponse<?>

use

BaseVersionResponse<? extends BaseVO>

Since you know that the return type will be one of those things that implements the interface/class.

GoGoCarl
  • 2,519
  • 13
  • 16
  • Well, if I change it to `` then I get a compilation error on this line: `baseVersionResponse = createVersionResponse(version);` stating this: **Type mismatch: cannot convert from BaseVersionResponse to BaseVersionResponse**. Which then leads me to look closer at that method which still has the > unbounded wildcards. And if I change _those_ to then of course my factory no longer works and I get this error **Type mismatch: cannot convert from VersionResponseV1 to BaseVersionResponse** on this line: `... = new VersionResponseV1();`. – risingTide Mar 19 '15 at 01:26
  • Ah, missed that detail. I updated my answer. This will now restrict you to explicitly using subclasses of BaseVO, which is what you want for this particular case anyway. And you should get the same result now. – GoGoCarl Mar 20 '15 at 04:01
  • Hmm, I'm still getting the same compilation error. I updated my original code above with your suggestions though so that we're looking at the same thing. Any other ideas? I feel like I _should_ be able to work somehow... – risingTide Mar 20 '15 at 14:13
  • I'm going to craft up a better answer. I think there's a sticking point in this one that's fundamentally flawed. – GoGoCarl Mar 20 '15 at 16:42
1

Ok, I took a better look at this today. The problem is that the wildcard, while the right way to go, precludes you from doing:

BaseVO versionVO = createVersionVO(version);

Because the populate call wants an extension of BaseVO, not an actual BaseVO, which doesn't qualify. That means you can't pass that versionVO variable directly.

So, to keep the type checking in place, which I think is good because you'll always want an implementation, leave pretty much everything as-is above, and change your BaseVersionResponse class to something like:

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

}

So, populate method now takes a BaseVO, and there's a new getVersion method to do some explicit casting for us. This should be ok since we know that the factory will always supply the right thing, but if another caller doesn't, an IllegalArgumentException is thrown.

Now, in your response class implementation, change the populate method accordingly:

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

So, we've changed the populate method to take BaseVO, and the getVersion method does the casting for us. All the other type checks still apply, and we're good to go.

The casting makes it feel not as clean, but for the factory approach you're using, it's really the only way (I can think of) to keep the guarantees made by the type declarations and the code pattern in tact.

Hope that helps!

GoGoCarl
  • 2,519
  • 13
  • 16
  • This does help; I actually have working code now! Thank you. I do feel that it is not as clean because of the explicit casting; I was actually trying to avoid that. Perhaps there's some way that I can modify what I'm doing with my factory so that it may be cleaned up? I have to look into that. Thanks again...this does solve the original problem I posted. – risingTide Mar 20 '15 at 18:46
  • Glad to help. If you do come across an alternative, I'd be interested in hearing about it. But like you said, I think you would have to significantly modify your approach to get a "cleaner" result. As is, the main cases are handled, and the edge cases are taken care of appropriately, and the code doesn't get in the way, so should be sufficient. But it's definitely an interesting question. – GoGoCarl Mar 20 '15 at 21:03