来自同一实例的通用捕获不能推断为相等

时间:2018-04-30 15:01:52

标签: java generics jackson

我有以下情况:

public interface BaseConfig {
}

public abstract class BaseWidget<C extends BaseConfig> {
    public abstract C getConfig();
    public abstract void setConfig(C config);
    public abstract Class<C> getConfigClass();
}

public class ConcreteConfig implements BaseConfig {
    public String foo = "bar";
}

public class ConcreteWidget extends BaseWidget<ConcreteConfig> {
    private ConcreteConfig config;

    public ConcreteWidget(ConcreteConfig config) {
        this.config = config;
    }

    @Override
    public ConcreteConfig getConfig() {
        return config;
    }

    @Override
    public void setConfig(ConcreteConfig config) {
        this.config = config;
    }

    @Override
    public Class<ConcreteConfig> getConfigClass() {
        return ConcreteConfig.class;
    }
}

我希望能够将BaseConfig的实例一般序列化和反序列化为BaseWidget实现指定的正确类型。以下作品:

final BaseWidget<ConcreteConfig> instance = new ConcreteWidget(new ConcreteConfig());
final BaseConfig config = instance.getConfig();
final String configJson = om.writeValueAsString(config);

instance.setConfig(om.readValue(configJson, instance.getConfigClass()));

现在如果我尝试使上面的代码通用(消除ConcreteConfig的出现,所以第一行的类型为BaseWidget<? extends BaseConfig>),我在最后一行得到以下编译错误:< / p>

  

错误:(18,40)java:不兼容的类型:BaseConfig无法转换为捕获#1的?扩展BaseConfig

或在IntelliJ中:

  BaseWidget中的

setConfig(capture&lt;?extends BaseConfig&gt;)无法应用于(捕获&lt;?extends BaseConfig&gt;)

我知道错误告诉我两个捕获是不兼容的,但为什么会这样?据我所知,instance.setConfig(C)instance.getConfigClass() -> Class<C>的类型必须生成相同的C,因为它们来自同一个实例。

我可以通过BaseWidget使用其他方法代替setConfig来解决此问题。

public abstract void readConfig(ObjectMapper mapper, String rawConfig) throws IOException;

并在ConcreteWidget

中实现
@Override
public void readConfig(ObjectMapper mapper, String rawConfig) throws IOException {
    this.config = mapper.readValue(rawConfig, ConcreteConfig.class);
}

但我宁愿不让小部件依赖任何序列化逻辑。

这是我的测试专家项目:https://drive.google.com/open?id=16zt2FC8Gs5Cke-kbYBDRhmytE5utVn8E

2 个答案:

答案 0 :(得分:0)

另一项工作如下:

final BaseWidget<ConcreteConfig> instance = new ConcreteWidget(new ConcreteConfig());

答案 1 :(得分:0)

为了说服编译器两个类型C是相同的,我需要为上面的操作引入一个类型名称。在我的情况下,将代码提取到这样的方法就可以了:

private static <C extends BaseConfig> void parseSetConfig(BaseWidget<C> instance, String configJson) throws IOException {
    final C config = om.readValue(configJson, instance.getConfigClass());
    instance.setConfig(config);
}

可以使用通配符BaseWidget<? extends BaseConfig>调用此方法就好了:

final BaseWidget<? extends BaseConfig> instance = new ConcreteWidget(new ConcreteConfig());
final BaseConfig config = instance.getConfig();
final String configJson = om.writeValueAsString(config);

parseSetConfig(instance, configJson);