AbstrakcjaCzęść NR.14

Klasa abstrakcyjna dostarcza implementacje innym klasom. Klasa abstrakcyjna jest więc taką podstawką dla pozostałych klas.

Klasa abstrakcyjna może zawierać zestaw kompletnych metod, które będzie posiadać klasa pochodna. Klasa abstrakcyjna może też mieć zestaw abstrakcyjnych metod, które nie są zdefiniowane w swoim działaniu. Podobnie do interfejsów, w takich metodach istnieje tylko sygnatura metod. Nie ma w nich ciała.

Poniżej znajduje się klasa Shape, która posiada pole oraz abstrakcyjną metodę. Do klasy abstrakcyjnej można umieszczać te same elementy co w normalnej klasie, plus metody abstrakcyjne.

Aby zdeklarować klasę abstrakcyjną trzeba przed słowem class napisać “abstract”.

abstract class Shape
{
    public int x = 100, y = 100;
    public abstract int getArea();
}

Metodę abstrakcyjną oznacza się poprzez dopisanie słowa abstract po poziomie dostępu.

W C# do klasy abstrakcyjnej można dodać bez problemu wiele innych elementów jak do zwykłej klasy. W C# właściwości to tak naprawdę metody GET/SET, co oznacza, że można tworzyć w klasie abstrakcyjnej abstrakcyjne właściwości.

Abstrakcyjne też mogą być indeksery i zdarzenia.

abstract class Shape
{
    // Abstract method
    public abstract int GetArea();

    // Abstract property
    public abstract int area { get; set; }

    // Abstract indexer
    public abstract int this[int index] { get; set; }

    // Abstract event
    public delegate void MyDelegate();
    public abstract event MyDelegate MyEvent;

    // Abstract class
    public abstract class InnerShape {};

}

Wewnątrz klasy abstrakcyjnej można zagnieździć kolejną klasę abstrakcyjną. W C# i Javie jest to możliwe.

image

Posiadanie zagnieżdżonej klasy abstrakcyjnej średnio ma sens, ale jej działanie i użycie wyglądałoby tak.

public class Rect extends Shape 
{
    @Override
    public int GetArea() {
        // TODO Auto-generated method stub
        return 0;
    }
    
    public class InnerRect extends InnerShape {}
}

Przykład abstract

Jak już zadeklarowaliśmy klasę abstrakcyjną, to co się stanie, gdy klasa będzie chciała dziedziczyć po takiej klasie.

abstract class Shape
{
    private int x = 100, y = 100;
    public abstract int GetArea();
}

Klasa pochodna jest zmuszona zaimplementować abstrakcyjne metody lub w przypadku C# wszystko inne, co może być abstrakcyjne.

class Rectangle extends Shape
{
    @Override public int getArea()
    {
        return x * y;
    }
}

W C# do określenia dziedziczenia używamy symbolu dwukropka, zamiast słowa kluczowego extends.

class Rectangle : Shape
{
    public int GetArea() { return x * y; }
}
Shape s = new Rectangle();

Normalnie nie możemy utworzyć instancji klasy abstrakcyjnej. Podobnie jest z interfejsami. Możemy jednak do zmiennej umieścić klasę pochodną, nieabstrakcyjną do zmiennej, która referuje się do klasy bazowej, która jest abstrakcyjna.

Klasa abstrakcyjna może dziedziczyć po klasie nieabstrakcyjnej.

class NonAbstract {}
abstract class Abstract extends NonAbstract {}

class NonAbstract {}
abstract class Abstract : NonAbstract {}

W przypadku C# i słowa kluczowego virtual. Istnieje możliwość nadpisania metody wirtualnej, aby uczynić ją metodą abstrakcyjną. Wymuszając na klasach pochodnych konieczność implementowania metod, które były wcześniej tylko wirtualne.

class MyClass
{
    void virtual Dummy() {}
}

abstract class Abstract : MyClass
{
    void abstract override Dummy() {}
}

Gdy jedna klasa abstrakcyjna dziedziczy po kolejnej klasie abstrakcyjnej. Klasa pochodna abstrakcyjna nie musi implementować metody abstrakcyjnej.

abstract class  AbstractSec {    
    
    // Abstract method
    public abstract int Whatever();
}

abstract class Abstract extends AbstractSec {}

Tak jak mówiłem wcześniej nie można utworzyć instancji klasy abstrakcyjnej.

Shape s = new Shape(); // compile-time error

To wszystko co trzeba wiedzieć.

Abstrakcyjne klasy i interfejsy

Abstrakcyjne klasy i interfejsy są do siebie podobne na parę sposobów. Mogą definiować sygnatury metody i wymuszać na klasach pochodnych ich implementacje. Interfejsy i klasy abstrakcyjne nie mogą być utworzone.

// Definuje pola i metodę abstrakcyjną 
abstract class Shape
{
    public int x = 100, y = 100;
    public abstract int GetArea();
}

class Rectangle : Shape {} // class is a Shape

// Definiuje interfejs - specyficzną implementacje
interface ISum
{
    int SumTo();
}

class PeterClass : ISum {} 

Kluczowa różnica polega na tym, że w klasie abstrakcyjnej można deklarować nieabstrakcyjne metody, czyli normalne metody, które mogłyby być w normalnej klasie.

Druga różnica polega na tym, że klasa abstrakcyjna jest klasą, a to oznacza, że jedna klasa może dziedziczyć tylko po jednej klasie, nieważne czy jest abstrakcyjna czy nie.

Dziedziczenie interfejsów jest nieograniczone.

Trzecia różnica polega na tym, że interfejs nie może dziedziczyć po klasie. Interfejs może dziedziczyć po innym interfejsie.

Kiedy używać klas abstrakcyjnych a kiedy interfejsów

lnterfejs jest używany po to, aby definiować funkcjonalność klasy, bądź określać funkcjonalność, którą wiele klas musi posiadać.

Klasa abstrakcyjna natomiast daje nam częściową implementację klasy. Klasy pochodne, klasy abstrakcyjne mają więc te same metody, które działają tak samo dla wszystkich, ale też mają metody uzupełnione zależnie od klasy pochodnej.