Производни класове и наследяване

В Java класовете са подредени в единна дървовидна структура. Един клас може да бъде деклариран като подклас (или производен клас) чрез ключовата дума  extends. Производният клас наследява данните и методите на своя супер-клас (базов клас) и ги използва все едно, че ли са декларирани при него. В Java всички класове са производни на един базов клас Object. Следователно всеки клас наследява неговите компоненти.

class SupClasse{
        float fl;
        …
        void func() {…}
        …
}

class ExtClasse extends SupClasse{ //fl и func() наследени
        int in;
        …
        void func2(){…};
        …
}

В този пример всеки обект от тип "ExtClasse" притежава едновременно променливата fl и метода func().

Java разрешава само просто наследяване. Това означава, че всеки клас може да има само един непосредствен базов клас.

Производният клас от своя страна също може да бъде наследен. Обикновенно механизмът на производните класове се използва за специализация на даден клас, като му се добавят нови свойства чрез добавяне на на променливи и методи. 

Предефиниране на променливи

В един производен клас може да бъде дефинирана променлива, с име съвпадащо с променлива, която вече е декларирана в базовия му клас. В този случай, тя маскира променливата от базовия клас.Двете променливи продължават да съществуват. В производния клас методите от базовия клас виждат променливата от базовия клас. докато методите дефинирани в производния - новата променлива. Изборът на променливата се извършава по статичен начин при компилацията на програмата.

class SupClasse{
        int s;
        …
}

class ExtClasse extends SupClasse{
        int s;
        …
}

Ако в метод дефиниран в производния клас трябва да се извика променливaта от базовия клас, се използва ключовата дума super:

        int s1 = super.s;

Предефиниране на методи

В един същи клас методите с едно и също име трябва да се различават по аргументите си (презареждане, overloading). В производните класове обаче е възможен и друг начин на предефиниране - създаване на функция с еднакъв прототип (име, аргументи и резултат) с функция от базовия клас (overriding). В този случай в производния клас предефинирания метод замества метода от базовия клас, което позволява промяна на поведението на обектите от производния клас. Това е друга форма на полиморфизъм.

 
пример стартиране с:    java Shapes

class Shape {
    public String toString() { return "Shape"; }
}

class Circle extends Shape {
    public String toString() { return "Circle"; }
}

class Square extends Shape {
    public String toString() { return "Square"; }
}

class Triangle extends Shape {
    
    public String toString() {
        System.out.print(super.toString()+ "-->");
        return "Triangle"; }
}

public class Shapes{
    public static void main(String a[]){
        Shape[] sh = {new Shape(),new Circle(), new Square(), new Triangle()};
        for (int i=0; i< sh.length; i++)
            System.out.println(i+": "+sh[i]);
    }
}
  0: Shape
  1: Circle
  2: Square
  Shape-->3: Triangle

В първия случай (различни аргументи) изборът на метода за изпълнение се осъществява в процеса на компилацията ( early binding; static link). Във втория случай изборът се осъществява по време на изпълнение на програмата(late binding).


Извикване на конструкторите


class Shape {
    Shape(){
        System.out.println("\nConstructing Shape");
    }
    public String toString() { return "Shape"; }
}

class Circle extends Shape {
    Circle(){
        System.out.println("Constructing Circle");
    }
    public String toString() { return "Circle"; }
}

class Square extends Shape {
    Square(){
        System.out.println("Constructing Square");
    }
    public String toString() { return "Square"; }
}

class Triangle extends Shape {
    public String toString() { return "Triangle"; }
}

public class Shapes{
    public static void main(String a[]){
        Shape[] sh = {new Shape(),new Circle(), new Square(), new Triangle()};
        for (int i=0; i< sh.length; i++)
            System.out.println(i+": "+sh[i]);
    }
}

Constructing Shape

Constructing Shape
Constructing Circle

Constructing Shape
Constructing Square

Constructing Shape
0: Shape
1: Circle
2: Square
3: Triangle

Конструктори с параметри


class Shape {
    double perim;
    Shape(double p){
        this.perim = p;
        System.out.println("\nConstructing Shape with perim="+perim);
    }
    Shape(){
        System.out.println("\nConstructing Shape");
    }
    public String toString() { return "Shape"; }
}

class Circle extends Shape {
    Circle(){
        System.out.println("Constructing Circle");
    }
    public String toString() { return "Circle"; }
}

class Square extends Shape {
    Square(){
        System.out.println("Constructing Square");
    }
    public String toString() { return "Square"; }
}

class Triangle extends Shape {
    double med;
    Triangle(double p, double m){
        super(p);
        this.med=m;
        System.out.println("Constructing Triangle perim="+perim+" med="+med);
    }
    Triangle(){
        System.out.println("Constructing Triangle");
    }
    public String toString() { return "Triangle"; }
}

public class Shapes{
    public static void main(String a[]){
        Shape[] sh = {new Shape(12.2),new Circle(), new Square(), new Triangle(6.4, 3.3)};
        for (int i=0; i< sh.length; i++)
            System.out.println(i+": "+sh[i]);
    }
}

Constructing Shape with perim=12.2

Constructing Shape
Constructing Circle

Constructing Shape
Constructing Square

Constructing Shape with perim=6.4
Constructing Triangle perim=6.4 med=3.3
0: Shape
1: Circle
2: Square
3: Triangle

Друг пример

public class Bc {
    int s=4;
    void prt(){System.out.println("prt in Bc - s ="+s);}
}
public class Ec extends Bc{
    int s=12;
    void prt(){System.out.println("prt in Ec - s="+s+" super.s=" +super.s);}
}
public class Test {
    public static void main(String a[]){
        Ec e= new Ec();
        Bc b=e;
        Bc b2= new Bc();
        e.prt();
        b.prt();
        b2.prt();
        System.out.println("e.s="+e.s);
        System.out.println("((Bc)e).s="+((Bc)e).s);
        System.out.println("b.s="+b.s);
        System.out.println("((Ec)b).s="+((Ec)b).s);
        System.out.println("((Ec)b2).s="+((Ec)b2).s);
    }
}
prt in Ec - s=12 super.s=4
prt in Ec - s=12 super.s=4
prt in Bc - s =4
e.s=12
((Bc)e).s=4
b.s=4
((Ec)b).s=12
Exception in thread "main" java.lang.ClassCastException: Bc cannot be cast to Ec
    at Test.main(Test.java:14)

Абстрактни методи и класове

Както в  C++, един метод може да бъде дефиниран с модификатора abstract с което се специфицира, че се отнася само до прототип. Абстрактните методи нямат описание:

        abstract type nameMethod (type nameParameter, …);

В Java, всеки клас, който съдържа един или повече абстрактни методи, трябва явно да бъде деклариран като абстрактен:

abstract class NameClass{
        …
        abstract int methode abstr (int par1);
        …
}

Той може да съдържа други методи, които не са абстрактни, също така може да съдържа и декларации на променливи.

От абстрактен клас не могат да се създават обекти. За да може да се използува трябва да се създаде негов производен клас (класове) в който да бъдат предефинирани всички абстрактни методи с методи, които не са абстрактни. Не е задължително всички абстрактни методи да се дефинират в непосредствено производния клас, но ако в един производен клас не са предефинирани всички абстрактни методи, то производният клас също е абстрактен и трябва да бъде дефиниран като такъв.

Абстрактните методи осигуряват гъвкав инструмент за създаване на производни класове за специфични нужди. Например класът java.io.InputStream притежава един абстрактен метод наречен read(). Производните му класове прилагат метода по свой начин за въвеждане на различни типове данни. Останалата част от класа осигурява разширената му функционалност, базирана върху метода read().

class Shape {

Color color; // Color of the shape. (

void setColor(Color newColor) {
// Method to change the color of the shape.
color = newColor; // change value of instance variable
redraw(); // redraw shape, which will appear in new color
}

abstract void redraw(); {

} // end of class Shape
class Rectangle extends Shape {
void redraw() {
. . . // commands for drawing a rectangle
}
. . . // possibly, more methods and variables
}
class Oval extends Shape {
void redraw() {
. . . // commands for drawing an oval
}
. . . // possibly, more methods and variables
}
class RoundRect extends Shape {
void redraw() {
. . . // commands for drawing a rounded rectangle
}
. . . // possibly, more methods and variables
}