DziedziczenieCzęść NR.10

Dziedziczenie pozwala klasie na uzyskanie elementów (pól, metody) innej klasy. W przykładzie poniżej klasa kwadrat dziedziczy po klasie prostokąt.

Prostokąt staje się klasą bazową Kwadratu.

W dodatku wszystkie elementy klasy Prostokąt, jeśli są one dostępne stają się elementami klasy Kwadrat. Wyjątkami są konstruktor i w przypadku C# destruktory.

// Klasa bazowa (klasa rodzica)
class Rectangle
{
    public int x = 10, y = 10;
    public int GetArea() { return x * y; }
}
// Klasa pochodna (klasa dziecka)
class Square : Rectangle { } 

A oto analogiczny przykład o dziedziczeniu. Owoc jest super klasą/ klasą bazową klasy jabłko.

// Klasa bazowa (klasa rodzica)
class Fruit
{
    public String flavor;
}
// Klasa pochodna (klasa dziecka)
class Apple extends Fruit
{
    public String variety;
}  

Obiekt

W Javie jak i w C# jakakolwiek klasa dziedziczy po Obiekcie. Obiekt jest więc korzeniem głównym wszystkich klas.

class Fruit extends Object {} 

W C# w przeciwieństwie do Javy istnieje ujednolicony system typów danych. Co to oznacza? Oznacza to, że wszystko nie tylko klasy dziedziczą od klasy Object.

Czy naprawdę wszystko?

Czy naprawdę wszystko? W C# istnieje parę dziwnych typów, które nie mogą być zamienione na obiekty. Oficjalnie mogą być one wpierane przez wskaźniki. Oto dwa z nich : TypedReference, RuntimeArgumentHandle.

Te typy wywodzą się od metod C++/C i raczej nie musisz się nimi martwić.

Java i jej typy proste nie są obiektowo zorientowane. Dowodzi to fakt, że typ prosty INT w Javie nie ma metod z klasy Object.

image

W Javie poniższy kod nie mógł zostać wykonany. A przynajmniej tak było, aż do JDK 5.0

Object x = (int)a; 

image

W JDK 5.0 został przestawiona technika automatycznego rzutowania.

Jednakże typy proste w Javie magicznie nie stały się obiektami. Ukrycie, po prostu wykonuje się kod rzutowania.

Oznacza to, że i C# i Java w tym wypadku zachowują względem piszącego kod tak samo. Od wewnątrz ten mechanizm jest trochę inaczej obsłużony.

Typy, które dziedziczą po obiekcie posiadają zestaw podstawowych metod jak np. ToString(). W C# jest to każdy typ.

image

Rzutowanie

Koncepcja dziedziczenia pozwala nam rzutować dany typ na jego klasę bazową, jeśli taka istnieje.

Oznacza to, że klasa typu Rectangle może też być Obiektem. Może ona zostać użyta wszędzie tam, gdzie obiekt albo Rectangle są potrzebne i spodziewane.

Jeśli zostanie utworzona instancja typu Square, czyli kwadratu to może ona zostać rzutowana do typu Rectangle (prostokątu), gdyż ten typ posiada wszystkie właściwości Square (kwadratu).

Square s = new Square();
Rectangle r = s; 
Apple a = new Apple();
Fruit f = a; 

Obiekt ten jest teraz widoczny jako Rectangle i może on korzystać tylko z właściwości Rectangle.

Analogicznie Apple może wykonywać tylko zdolności klasy Fruit.

f.flavor = "Sweet";

Gdy obiekt ten zostanie rzutowany ponownie do Square kwadratu, to zachowa wszystkie swoje właściwości, które miał poprzednio.

Rzutowanie nie zmienia obiektu w żaden sposób. Rzutowanie może tylko ograniczyć jego możliwości, gdy jest referowane jako klasa bazowa.

Square s2 = (Square)r; 
Apple b = (Apple)f; 

Rzutowanie tak zwane “Downcast” musi być określone jawnie. W końcu nie każdy prostokąt Rectangle przechowuje Square kwadrat.

Jeśli tak nie będzie zostanie rzucony wyjątek.

Rectangle r2 = new Rectangle();
Square s3 = (Square)r2; // możliwy błąd w tym wypadu on wystąpi 

C# i słowa kluczowe IS i AS

W C# do bezpiecznego rzutowania mamy dwa słowa kluczowe.

Pierwszym z nich jest operator IS.

Zwróci on prawdę, jeżeli lewa strona obiektu może zostać rzutowana na typ określony po prawej stronie.

W przypadku niepowodzenia nie ma rzucania wyjątków.

Rectangle q = new Square();
if (q is Square) { Square o = q; } // condition is true 

Operator AS wykona rzutowanie na dany obiekt i zwróci jego referencję. Jeśli rzutowanie nie może się udać zwróci on wartość NULL.

Rectangle r = new Rectangle();
Square o = r as Square; // invalid cast, returns null 

Java i operator InstanceOf

W Javie, aby bezpiecznie sprawdzić, czy dany obiekt może być rzutowany na daną klasę używamy operatora InstanceOf.

Ten operator zwróci wartość TRUE, gdy lewa strona wyrażenia może zostać rzutowana na prawą stronę określającą dany typ.

W wypadu niepowodzenia nie ma żadnych wyjątków.

Apple c = (f instanceof Apple) ? (Apple)f : null; 

Boxing i UnBoxing i automatyczne pakowanie : Java a C#

O ten temat już zahaczyłem trochę. Ujednolicony system typów w C# pozwala zmiennej i jej wartości niejawnie przekonwertować się na typ referencyjny. Ta operacja nazywa się Boxing czyli pakowanie.

Gdy wartość zostanie skopiowana do obiektu, jest ona traktowana jak typ referencyjny.

int myInt = 5;
object myObj = myInt; // boxing

Operacją przeciwną do pakowania jest operacja Unboxing, czyli rozpakowywanie.

Konwertuje on wartość typu referencja na typ wartościowy o ile jest to możliwe. W przeciwnym wypadku mamy wyjątek.

myInt = (int)myObj; // unboxing 

Wraz JDK 5.0 powstała koncepcja w Javie związana z Automatycznym wypakowywaniem/pakowaniem.

Java posiada typy proste jak: int, long,double, byte, boolean.

Java jednak też posiada otoczki (wrappery), które otaczają typy proste w formie obiektu/klasy czyniąc je typami referencyjnymi.

(Integer, Long, Double, Byte)

Mimo tej różnicy kod przedstawiony poniżej może też być użyty w Javie.

Java automatycznie zamieni typ int na typ Integer, a potem umieści go obiektu.

Przy wypakowaniu pobierze wartość typu Integer i prześle go typu int.

Nie trzeba więc pisać takiego kodu.

int intValue = 0;
Integer intObject = intValue;
Object obj = intObject;

int antoher = ((Integer)intObject).intValue();