Объектно ориентированное программирование наследственность
ребята, помогайте не очень умному как мы смогли отправить в барбершоп объекты типа cat и dog, если shear принимает в качестве параметра объекты типа animal? Почему это возможно? код из статьи: public class AnimalBarbershop < public void shear(Animal animal) < System.out.println("Стрижка готова!"); >> public static void main(String[] args) < Cat cat = new Cat(); Dog dog = new Dog(); AnimalBarbershop barbershop = new AnimalBarbershop(); barbershop.shear(cat); barbershop.shear(dog); >
полиморфизм тут описан как то странно. и вообще как правило это самая плохообъясненная часть ООП, у него нет четкого определения и везде его объясняют по разному. как я понимаю ПОЛИМОРФИЗМ — это возможность для разных классов, имеющих общее свойство, реализовать это свойство по своему. ну допустим птица и самолет, оба умеют летать, поэтому у обоих должен быть какой то метод описывающий эту способность. fly к примеру. поэтому у обеих будет интерфейс который декларирует это свойство. но летают они по разному, поэтому в каждом случае и реализация будет разной. Это и есть полиморфизм — есть классы с общим свойством, оно абстрактное, но у разных классов (конкретных объектов) есть конкретная реализация этого свойства, и она будет разной, в зависимости от объекта
Я не знаю почему абстракцию тут пишут как 4й принцип ООП. Есть разные приемы ООП, такие как ассоциация, агрегация, композиция и еще некоторые комбинации, абстракция это очень общее понятие, но в классических трудах все равно выделяют наследование, инкапсуляцию и полиморфизм. Хотя, как мне кажется, именно полиморфизм пришел с ООП, первые 2 уже были в С в виде «хаков». Сорри если оффтоп.
Введение в ООП с примерами на C#. Часть вторая. Все, что нужно знать о наследовании
В первой статье этой серии мы рассматривали работу разных вариантов реализации перегрузки. В этой части мы сосредоточимся на таком разделе объектно-ориентированного программирования, как наследование.
Давайте сразу тезисно опишем, что такое наследование:
- Это механизм создания нового класса на основе уже существующего старого.
- Старый класс называется «родительским», «предком» («super class»).
- Новый класс называется «дочерним», «наследником» («sub class»).
- Наследование нужно для повторного использования кода, которое облегчает следование принципу DRY (Don’t Repeat Yourself — Не повторяйся).
- Дочерний класс содержит методы и переменные родительского.
Рассмотрим наследование в действии
Создайте консольное приложение и назовите его InheritanceAndPolymorphism . Добавьте два класса, с названиями ClassA и ClassB , как показано ниже:
ClassA: class ClassA < >ClassB: class ClassB < public int x = 100; public void Display1() < Console.WriteLine("ClassB Display1"); >public void Display2() < Console.WriteLine("ClassB Display2"); >>
Как вы можете видеть, класс A пуст, а в B мы добавили два метода и переменную x со значением 100.
Теперь в главном методе Program.cs напишите следующее:
Разумеется, этот код вызовет ошибку:
Error: ‘InheritanceAndPolymorphism.ClassA’ does not contain a definition for ‘Display1’ and no extension method ‘Display1’ accepting a first argument of type ‘InheritanceAndPolymorphism.ClassA’ could be found (are you missing a using directive or an assembly reference?)
Очевидно, причина в том, что в классе А нет метода, который мы вызываем. Однако он есть у класса B. Было бы здорово, если бы мы могли получить доступ ко всему коду в B из A!
Теперь измените описание первого класса на следующее:
ClassA: class ClassA:ClassB
Теперь после выполнения программы мы получим:
Т.е. теперь ClassA наследует публичные методы из ClassB , это то же самое, если бы мы скопировали весь код из B в A. Всё, что объект класса B может делать, может и объект класса A. ClassA — дочерний класс, а ClassB — родительский.
Что нужно запомнить: как сын получается похожим на отца, наследует его черты, так и дочерний класс имеет параметры родительского.
Теперь давайте представим, что ClassA тоже имеет метод Display1 :
Что будет, если мы запустим код теперь? Каким будет вывод? И будет ли вывод вообще или выйдет ошибка компиляции? Давайте проверим.
Однако мы также получим предупреждение:
Warning: ‘InheritanceAndPolymorphism.ClassA.Display1()’ hides inherited member ‘InheritanceAndPolymorphism.ClassB.Display1()’. Use the new keyword if hiding was intended.
Что нужно запомнить: ничто не может помешать создать в дочернем классе такой же метод, как и в родительском.
Когда мы вызываем a.Display1() , C# сначала ищет Display1() в ClassA , а только потом в ClassB . Поскольку в A такой метод есть, вызывается именно он.
Что нужно запомнить: методы дочерних классов имеют приоритет при выполнении.
Такая возможность нам даётся для того, чтобы мы могли изменить поведение методов предка, если оно нас не устраивает. Однако мы всё равно можем вызывать методы родительского класса следующим образом:
ClassA: class ClassA:ClassB < public void Display1() < Console.WriteLine("ClassA Display1"); base.Display1(); >>
В таком случае вывод будет:
Что нужно запомнить: ключевое слово base может быть использовано для обращения к методам класса-предка.
Что же, вверх по иерархии мы обращаться можем. Давайте попробуем сделать наоборот:
/// /// ClassB: выступает в роли класса-предка /// class ClassB < public int x = 100; public void Display1() < Console.WriteLine("ClassB Display1"); >> /// /// ClassA: выступает в роли класса-наследника /// class ClassA : ClassB < public void Display2() < Console.WriteLine("ClassA Display2"); >> /// /// Program: используется для выполнения кода. /// Contains Main method. /// class Program < static void Main(string[] args) < ClassB b = new ClassB(); b.Display2(); Console.ReadKey(); >>
Error: ‘InheritanceAndPolymorphism.ClassB’ does not contain a definition for ‘Display2’ and no extension method ‘Display2’ accepting a first argument of type ‘InheritanceAndPolymorphism.ClassB’ could be found (are you missing a using directive or an assembly reference?)
Что нужно запомнить: наследование не работает в обратном направлении.
Когда класс A наследуется от B, он получает все его методы и может их использовать. Однако методы, которые были добавлены в A, не загружаются наверх в B, наследование не имеет обратной совместимости. Если попытаться вызвать из класса-родителя метод, который создан в классе-наследнике, вы получите ошибку.
Что нужно запомнить: кроме конструкторов и деструкторов, дочерний класс получает от родителя абсолютно всё.
Если класс С, будет унаследован от класса B, который, в свою очередь, будет унаследован от класса A, то класс C унаследует члены как от класса B, так и от класса A. Это транзитивное свойство наследования. Потомок перенимает все члены родителей и не может исключить какие-либо. Он может «спрятать» их, создав свой метод с тем же именем. Конечно, это никак не повлияет на родительский класс, просто в дочернем метод не будет виден.
Члены класса могут быть двух типов — статический, который принадлежит именно классу, или обычный, который доступен только из реализаций класса (его объектов). Чтобы сделать член статическим мы должны использовать ключевое слово static .
Если мы не наследуем класс ни от какого другого, подразумевается, что мы наследуем его от класса object . Это — родитель всех классов, и он единственный не унаследован ни от чего. Таким образом, такой код:
public class ClassB < >public class ClassA : ClassB
Автоматически воспринимается C# так:
public class ClassB:object < >public class ClassA : ClassB
Таким образом, по свойству транзитивности, ClassA также является наследником object .
Теперь ещё один момент. Если мы захотим сделать так:
public class ClassW : System.ValueType < >public class ClassX : System.Enum < >public class ClassY : System.Delegate < >public class ClassZ : System.Array
То у нас это не получится:
‘InheritanceAndPolymorphism.ClassW’ cannot derive from special class ‘System.ValueType’
‘InheritanceAndPolymorphism.ClassX’ cannot derive from special class ‘System.Enum’
‘InheritanceAndPolymorphism.ClassY’ cannot derive from special class ‘System.Delegate’
‘InheritanceAndPolymorphism.ClassZ’ cannot derive from special class ‘System.Array’
Заметили словосочетание «special class»? Такие классы нельзя расширять.
Что нужно запомнить: ваши классы не могут быть унаследованы от встроенных классов вроде System.ValueType , System.Enum , System.Delegate , System.Array и т.д.
public class ClassW < >public class ClassX < >public class ClassY : ClassW, ClassX
Выше мы описали три класса: ClassW , ClassX и ClassY , который наследуется от первых двух. Теперь попробуем это скомпилировать:
Compile time Error: Class ‘InheritanceAndPolymorphism.ClassY’ cannot have multiple base classes: ‘InheritanceAndPolymorphism.ClassW’ and ‘ClassX’.
Что ещё нужно запомнить: класс может иметь только одного родителя, множественное наследование в C# не поддерживается (оно поддерживается у интерфейсов, но в этой статье мы о них речи не ведём).
Если мы попробуем обойти это правило таким образом:
public class ClassW:ClassY < >public class ClassX:ClassW < >public class ClassY : ClassX
Error: Circular base class dependency involving ‘InheritanceAndPolymorphism.ClassX’ and ‘InheritanceAndPolymorphism.ClassW’.
Что нужно запомнить: классы не могут наследоваться циклически (1-й от 2-го, 2-й от 3-го 3-й от 1-го), что, в общем-то, логично.
Операции с объектами
ClassB: public class ClassB < public int b = 100; >ClassA: public class ClassA
/// /// Program: используется для запуска кода. /// Contains Main method. /// public class Program < private static void Main(string[] args) < ClassB classB = new ClassB(); ClassA classA = new ClassA(); classA = classB; classB = classA; >>
Здесь мы пытаемся приравнять объект от разных классов друг к другу.
Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassB’ to ‘InheritanceAndPolymorphism.ClassA’
Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassA’ to ‘InheritanceAndPolymorphism.ClassB’
Однако у нас это плохо получается. Даже несмотря на то, что они имеют одинаковые поля с одинаковыми значениями. Даже если бы эти поля имели одинаковые названия. C# работает с типами очень чётко — вы не можете приравнять два объекта от двух независимых классов. Однако, если бы класс A наследовался от B:
public class ClassA:ClassB
…мы бы продвинулсь немногим дальше:
Error: Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassB’ to ‘InheritanceAndPolymorphism.ClassA’. An explicit conversion exists (are you missing a cast?)
Как я уже говорил, C# подходит к вопросам типов очень дотошно. Класс A унаследован от B, значит, имеет все его поля и методы — при назначении переменной типа B объекта типа A проблем не возникает. Однако вы уже знаете, что в обратную сторону это не работает — в классе B нет полей и методов, которые могут быть в A.
Что нужно запомнить: вы можете назначить переменной родительского типа объект дочернего, но не наоборот.
Здесь нам наконец-то представляется шанс обмануть правило:
public class ClassB < public int b = 100; >public class ClassA:ClassB < public int a = 100; >/// /// Program: используется для запуска кода /// Contains Main method. /// public class Program < private static void Main(string[] args) < ClassB classB = new ClassB(); ClassA classA = new ClassA(); classB=classA; classA = (ClassA)classB; >>
Приведение типа здесь сработает, но только потому, что эти классы находятся в наследственных отношениях. Два обособленных непримитивных типа привести друг к другу нельзя.
Итак, наш последний блок кода:
/// /// Program: используется для запуска программы. /// Contains Main method. /// public class Program < private static void Main(string[] args) < int integerA = 10; char characterB = 'A'; integerA = characterB; characterB = integerA; >>
Error: Cannot implicitly convert type ‘int’ to ‘char’. An explicit conversion exists (are you missing a cast?)
Что нужно запомнить: можно конвертировать char в int . Нельзя конвертировать int в char (причина в том, что диапазон целого числа больше, чем символа).
Заключение
В этой части мы рассмотрели наследование. Мы попробовали запускать разные варианты кода, чтобы возможно глубже понять суть этого принципа. *этот текст будет изменён после перевода следующей статьи* In my next article, we’ll be discussing about run time polymorphism. Inheritance plays a very important role in run time polymorphism.
Вот что вы должны были запомнить за сегодня:
- как сын получается похожим на отца, наследует его черты, так и дочерний класс имеет параметры родительского;
- ничто не может помешать создать в дочернем классе такой же метод, как и в родительском;
- методы дочерних классов имеют приоритет при выполнении;
- ключевое слово base может быть использовано для обращения к методам класса-предка;
- наследование не работает в обратном направлении;
- кроме конструкторов и деструкторов, дочерний класс получает от родителя абсолютно всё;
- ваши классы не могут быть унаследованы от встроенных классов вроде System.ValueType , System.Enum , System.Delegate , System.Array и т.д.;
- класс может иметь только одного родителя, множественное наследование классов в C# не поддерживается;
- классы не могут наследоваться циклически (1-й от 2-го, 2-й от 3-го 3-й от 1-го), это невозможно чисто логически;
- вы можете назначить переменной родительского типа объект дочернего, но не наоборот;
- можно конвертировать char в int . Нельзя конвертировать int в char (причина в том, что диапазон целого числа больше, чем символа).
Напоминаем вам, что в первой статье этой серии вы можете прочитать о полиморфизме. Продолжайте учиться программировать с нами!