извините за титул, так как я не мог найти лучшего, поэтому любые изменения на этом будут оценены.
рассмотрим эти классы:
public interface GlobalDashlet {
public Collection<? extends GlobalDashletSetting> getSettings();
public void setSettings(Collection<? extends GlobalDashletSetting> settings);
}
public class Dashlet implements GlobalDashlet {
private Collection<DashletSetting> settings;
public Collection<DashletSetting> getSettings(){
return settings;
}
//This Wont Work
public void setSettings(Collection<DashletSetting> settings) {
this.settings = settings;
}
//This Will Work
public Collection<DashletSetting> getSettings(){
return settings;
}
}
public class DashletSetting implements GlobalDashletSetting {
}
Почему переопределение метода setter (я имею в виду способ, которым я это сделал) не работает (класс dashlet жалуется на нереализованный метод), но переопределение метода getter работает?
как я могу это исправить? Мне также нужно иметь возможность реализовать методы setter (например, как переопределить методы getter), потому что я должен выполнить сериализацию класса Dashlet с помощью jsonson mapper, и Джексон не может определить фактический тип и объект во время выполнения без дополнительной информации на суперклассах.
Причина в том, что типы возврата могут быть ковариантными.
При переопределении метода вы всегда можете вернуть более конкретный тип (т.е. Подтип). Более понятным примером является следующее:
class NumberProvider {
Number getNumber() { return 1.23; }
}
class IntegerProvider extends NumberProvider {
// Returning a more specific type when overriding:
@Override
Integer getNumber() { return 42; }
}
Тип Collection<DashletSetting>
является правильным подтипом Collection<? extends GlobalDashletSetting>
Collection<? extends GlobalDashletSetting>
. См. Раздел о том, какие отношения супер-подтипов существуют между экземплярами родовых типов? в разделе Общие вопросы.
Для сеттера это не работает. Короче, почему он не работает: он не безопасен для типа. Пример того, где безопасность типа нарушается, легко найти, хотя это может показаться немного надуманным с первого взгляда:
// This is the interface as it was defined:
public interface GlobalDashlet {
public void setSettings(Collection<? extends GlobalDashletSetting> settings);
}
public class Dashlet implements GlobalDashlet {
// Assume this was working:
public void setSettings(Collection<DashletSetting> settings) {
// Then you could add a "DashletSetting" here:
settings.add(new DashletSetting());
}
}
// But someone who CALLED this method may not have given it
// a Collection<DashletSetting>, but maybe a collection
// like Collection<SpecialGlobalDashletSetting>:
Collection<SpecialGlobalDashletSetting> settings = ...;
GlobalDashlet dashlet = new Dashlet();
// Based on the method signature that was defined in the interface,
// this would be possible:
dashlet.setSettings(settings);
// Now, the "settings" collection WOULD contain a simple "DashletSetting",
// although it should only contain "SpecialGlobalDashletSetting" instances
// This would cause a ClassCastException sooner or later
Пример может показаться немного запутанным. Опять же, это более интуитивно понятно с "простыми" типами, такими как Number
и Integer
, но это сводится к одной и той же проблеме: если для метода setter был разрешен более специфичный тип, тогда безопасность типа может быть нарушена.
Потому что у него разные подписи.
Collection<DashletSetting>
не является Collection<? extends GlobalDashletSetting>
Collection<? extends GlobalDashletSetting>
вам необходимо переопределить точную подпись, а не ее часть.
если у вас есть метод, который принимает Object
, вы не можете переопределить его с помощью метода, который принимает String
даже String
extends Object
в вашем случае, Collection<DashletSetting>
и Collection<? extends GlobalDashletSetting>
Collection<? extends GlobalDashletSetting>
- фактически разные классы, вы должны переопределить их с тем же классом.
геттер работает, так как он имеет одну и ту же подпись (то же имя метода и без параметров), это не так в установщике
you need to override the exact signature, not part of it.
не объясняет это? потому что так работает Java
Поскольку метод overrided не может ограничивать объем входной переменной. Исходный метод может принимать все, что расширяет класс GlobalDashletSetting
но, но метод overriden isrestricting только для одного подкласса класса GlobalDashletSetting
co-variant
возвращаемого типа метода при переопределении