Представьте, что у вас есть продукт под названием «Zebra», который может быть дополнен плагинами. Он находит плагины, ища библиотеки DLL в некоторых каталогах. Он загружает все эти DLL и использует отражение для поиска любых классов, которые реализуют IZebraPlugin
, а затем вызывает методы этого интерфейса для связи с плагинами.
Это делает его полностью независимым от какого-либо конкретного класса плагина - ему все равно, что представляют собой классы. Он заботится только о том, чтобы они соответствовали спецификации интерфейса.
Интерфейсы - это способ определения точек расширяемости, подобных этому. Код, который говорит с интерфейсом, более слабо связан - на самом деле он не связан вообще с каким-либо другим конкретным кодом. Он может взаимодействовать с плагинами, написанными годами позже людьми, которые никогда не встречались с оригинальным разработчиком.
Вместо этого вы можете использовать базовый класс с виртуальными функциями - все плагины будут получены из базового класса. Но это гораздо более ограничивает, потому что класс может иметь только один базовый класс, тогда как он может реализовать любое количество интерфейсов.
Когда вы расширяете класс, методы переопределяются, но поля скрыты. Динамическая диспетчеризация работает для методов, но не для полей. Почему язык разработан так, бог знает почему.
Dog.name
- скрывает Animal.name
, и это очень плохой шаблон для этого. Любая хорошая IDE предупредит вас о том, что вы это делаете.
Оба поля экземпляра существуют, и вы можете получить доступ к ним из Dog
как this.name
и super.name
.
Переменные не являются полиморфными в Java; они не переопределяют друг друга.
Изменить: для дальнейшей поддержки ответа Solver я помню, что мой учитель ООП утверждал, что когда вы создаете объект из Child class
с reference
of Parent class
, переменные в Child class
, которые не существуют в Parent class
, по-прежнему выделяются памятью во время выполнения, но не могут быть доступны, поскольку в Parent class
, которые могли получить доступ к переменной Child class
.
В принципе, когда родительский класс имеет дочерний элемент, дочерний класс должен полностью выглядеть как его родитель, иначе «Как вы можете назвать их родителем и ребенком?» правильно? В любом случае, дочернему классу разрешено иметь поведение, отличное от его родителя. Это действительно разумно и естественно.
Но если вы хотите переопределить атрибут из дочернего класса, вы можете сделать это с помощью механизма конструктора
Пример кода
class Animal{
String name;
public Animal(){
name = "Animal";
}
public Animal(String name){
this.name = name;
}
public void print(){
System.out.println("I am an: "+name);
}
}
class Dog extends Animal{
Dog(){
super("Dog");
}
public void print(){
System.out.println("I am a: "+name);
}
}
Вы увидите, что имя атрибута "Dog"
в классе Dog
передается через конструктор, который здесь можно вызвать конструктор родительского класса через ключевое слово super
.
Результаты:
Animal object name: Animal
Dog1 object name: Dog
Dog2 object name: Dog
I am an: Animal
I am a: Dog
I am a: Dog
В Java мы используем метод set и get для доступа к полю. В вашем примере у нас есть класс Dog, расширяющий класс Animal.
Но если вы объявите его как Animal, если вы вызываете непосредственно поле Amimal dog1 = new Dog();
, вы создаете экземпляр Dog, но объявляетесь как Animal, поэтому, когда вы вызываете dog1.name
, он дает вам значение Animal.
Поле Animal скрыто полем Dog, вы все равно можете получить доступ к полю Animal, указав его так же, как и вы.
Поведение, которое вы ожидаете, может быть достигнуто следующим образом:
public class Main{
public static void main(String args[]){
Animal animal = new Animal();
Animal dog1 = new Dog();
Dog dog2 = new Dog();
System.out.println("Animal object name: " + animal.name);
System.out.println("Dog1 object name: "+dog1.name);
System.out.println("Dog2 object name: " + dog2.name);
animal.print();
dog1.print();
dog2.print();
}
}
class Animal {
String name = "Animal";
public void print(){
System.out.println("I am an: "+name);
}
}
class Dog extends Animal{
public Dog() {
this.name = "Dog"
}
}
Нет, это не так. Переменные экземпляра - это свойства определенного класса и не подвержены непосредственно супер или подклассам и полиморфизму.
Вы можете получить доступ к в обоих полях, используя «super.name» и «this.name» в Dog, но если вы используете только «имя», то в Dog будет принимать участие. Если вы хотите другой, вам явно нужно вызвать супер. Обратите внимание, что я говорю о доступе к переменным в классе Dog .