родовые захваты из одного экземпляра не могут быть равны - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть следующий сценарий:

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>), я получу следующую ошибку компиляции в последней строке:

Ошибка: (18, 40) Java: несовместимые типы: BaseConfig не может быть преобразован в захват # 1 из? расширяет BaseConfig

или в IntelliJ:

setConfig (capture <? Extends BaseConfig>) в BaseWidget не может быть применен к (capture <? Extends BaseConfig>)

Я понимаю, что ошибка говорит мне, что два захвата несовместимы, но почему это так? Насколько я понимаю, типы 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);
}

но я бы предпочел, чтобы виджет не зависел от логики сериализации.

Вот мой тестовый проект maven: https://drive.google.com/open?id=16zt2FC8Gs5Cke-kbYBDRhmytE5utVn8E

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Чтобы убедить компилятор в том, что оба типа 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);
0 голосов
/ 30 апреля 2018

Другая работа будет выглядеть следующим образом:

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