Переопределить атрибуты
Когда вы переопределяете метод, вы должны сохранить подпись метода и не можете уменьшить его видимость. Теперь я пробовал, что происходит, когда я делаю это с помощью атрибутов. Я был удивлен - ЭТО РАБОТАЕТ! Посмотрите сами:
public class A {
public Integer myAttribute;
}
public class B extends A {
public String myAttribute;
}
public class Main {
public static void main(String[] args) {
B b = new B();
b.myAttribute = "myString";
((A) b).myAttribute = 1337;
System.out.println(b.myAttribute);
System.out.println(((A)b).myAttribute);
}
}
Таким образом, можно написать атрибут в подклассе с тем же именем attibute, что и в суперклассе, но вы можете использовать различную видимость (модификатор) и тип. Поэтому я бы сказал, что атрибуты в суперклассе и подклассе почти полностью независимы друг от друга.
Теперь, если вы действительно используете одно и то же имя атрибута в суперклассе и подклассе, вы фактически скрываете атрибут суперкласса. При доступе к атрибуту с использованием подкласса вы получаете атрибут подкласса. Но атрибут суперкласса тоже есть! Вы должны выполнить бросок, чтобы получить доступ к атрибуту суперкласса снаружи. Изнутри ключевое слово "супер" должно быть полезно.
Вопрос 1:
Теперь в основном у вас есть два разных атрибута с тем же именем. Разве это не перерыв LSP?
Почему я пришел к этой детали:
Я экспериментирую с собственной письменной основой сохранения. Я хочу прочитать полное внутреннее состояние объекта и сохранить это состояние. Я читал все значения атрибутов через рефлексию, начиная с подтипа и пересекая суперклассы.
Теперь, если в суперклассе и подклассе есть два атрибута с тем же именем, я должен помнить класс, который объявляет атрибут, чтобы он мог сопоставлять значения атрибутов с правильными атрибутами и восстанавливать состояние объекта.
Вопрос 2:
Любые другие идеи, как справиться с этой деталью?
Вопрос 3:
Как другие структуры персистентности справляются с этой деталью?
Я никогда не видел эту деталь в использовании. Возможно, написание двух атрибутов с одним и тем же именем является немного уродливым, но это возможно, и с ним может столкнуться любая инфраструктура persistence. Возможно, есть ситуации, когда этот метод может быть полезен.
Заблаговременно.
Ответы
Ответ 1
ORM обычно используют стандарт javabeans, который определяет "свойства". Свойства определяются с помощью метода чтения и/или записи, а не поля, которое они читают/записывают. В 99% случаев свойства являются полем + геттер и сеттер, но это не обязательно. ORM считывает эти свойства и обнаруживает только те поля, которые имеют getters & seters. Так как методы переопределены, а не затенены, проблема исчезла.
Это скорее проблема инкапсуляции, чем нарушение LSP. Полиморфизм не влияет на доступ к полю, поэтому, если у вас есть поле Foo foo = new FooSubclas();
foo.field the value of the
Foo`, это означает, что поведение не изменится.
Ответ 2
Вопрос 1: он не прерывает LSP, потому что переменные-члены не являются полиморфными.
Ответ 3
Вопрос 1: LSP около behavioral subtyping
, и я бы сказал, что наличие двух членов с тем же именем не изменяет самого поведения.
Вопрос 2: Обнаружьте его (вам все равно нужно отсканировать иерархию классов) и не разрешать его.
Ответ 4
Вопрос 2: Сериализовать/десериализовать только через getters/seters.
Вопрос 3: см. ответ 2.
Ответ 5
Вы не можете переопределить атрибут, а только скрыть его. Это буквально называют скрытыми полями в tutorial. Подробнее об этом blog.
Q1: Нет, он не прерывает LSP, любая ссылка на A a = new B(); a.myAttribute
будет по-прежнему ссылаться на A.myAttribute
, а не на B.myAttribute
.
Q2: Я попытался бы избежать этого, но если вам нужно, вы можете получить доступ к A.myAttribute
из B
с помощью ключевого слова super.myAttribute
.
Q3: Я думаю, что С# допускает то же самое, они используют base
вместо super
, но у меня нет компилятора С# под рукой, а документация на поле, скрывающаяся на С#, разрежена.