→ Slide 1

Dziedziczenie w języku Java

→ Slide 2

Dziedziczenie

Dziedziczenie pozwala klasie potomnej (podklasie, subclass) odziedziczyć pola i metody klasy bazowej (nadklasy, superclass).

Korzyści:

→ Slide 3

Dziedziczenie w Javie

Klasa potomna rozszerza (extends) klasę bazową, dodając nowe funkcjonalności lub modyfikując istniejące.

class Superclass {
    // pola i metody
}
 
class Subclass extends Superclass {
    // dodatkowe pola i metody
}
→ Slide 4

Diagram klas UML

Diagram klas UML - dziedziczenie

→ Slide 5

Przykład

public class Animal {
 
    protected String name;
 
    public Animal(String name) {
        this.name = name;
    }
 
    public void sound() {
        System.out.println("Jakiś dźwięk");
    }
}
public class Dog extends Animal {
 
    private boolean isTrained;
 
    public Dog(String name) {
        this.name = name; // dostęp do pola z klasy bazowej 
        this.isTrained = false;
    }
 
    public void train() {
        isTrained = true;
    }
}
→ Slide 6

Dziedziczenie a wielodziedziczenie

W Javie dozwolone jest tylko pojedyncze dziedziczenie klas

class A { }
class B extends A { }     // poprawne
// class C extends A, B { }  // niepoprawne

Dziedziczenie wielopoziomowe (A → B → C)

class A { }
class B extends A { }
class C extends B { }
→ Slide 7

Modyfikatory dostępu

→ Slide 8

Polimorfizm

Polimorfizm - ten sam interfejs (ta sama nazwa metody) może reprezentować różne zachowania, zależne od typu obiektu, na którym metoda jest wywoływana

→ Slide 9

Przesłanianie metod

class Animal {
    void sound() {
        System.out.println("Jakiś dźwięk");
    }
}
class Dog extends Animal {
    @Override
    void sound() {  
        System.out.println("Hau hau");
    }
}
→ Slide 10

Przeciążenie metody

class Parent {
    void method() { }
}
 
class Child extends Parent {
    void method(int a) { }
    void method(int a, int b) { }
}
→ Slide 11

Dziedziczenie konstruktorów

public class Parent {
    public Parent() {
        System.out.println("Konstruktor klasy bazowej");
    }
}
public class Child extends Parent {
    public Child() {
        super(); // Wywołanie konstruktora klasy bazowej
        System.out.println("Konstruktor klasy potomnej");
    }
}
→ Slide 12

Dostęp do składowych nadklasy

class Parent {
    int value = 10;
    void display() {
        System.out.println("To jest klasa nadrzedna.");
    }
}
 
class Child extends Parent {
    @Override
    void display() {
        super.display();
        super.value = 20; // dostęp do pola klasy nadrzędnej
        System.out.println("To jest klasa potomna.");
    }
}
↓ Slide 13

Java Object

Każda klasa dziedziczy pośrednio lub bezpośrednio po klasie Object, która jest korzeniem hierarchii klas.

Dzięki temu wszystkie klasy mają dostęp do metod zdefiniowanych w Object:

→ Slide 14

Klasy abstrakcyjne

abstract class Animal {
    abstract void sound();
 
    void sleep() {
        System.out.println("Zwierze spi");
    }
}
class Dog extends Animal {
    @Override
    void sound() {  
        System.out.println("Hau hau");
    }
}
→ Slide 15

Klasa abstrakcyjna Number

Metody abstrakcyjne:

public abstract int intValue();
public abstract long longValue();
public abstract float floatValue();
public abstract double doubleValue();

Metody wbudowane:

public byte byteValue() { return (byte) intValue(); }
public short shortValue() { return (short) intValue(); }
→ Slide 16

Interfejsy

public interface Animal {
    int PAWS_COUNT = 4;    // pole statyczne i finalne
    String sound();        // metoda abstrakcyjna
}
public class Dog implements Animal {
    public String food = "Dog food";   // pole niestatyczne
 
    @Override
    public String sound() { return "Hau hau"; }
}
→ Slide 17

Interfejsy w Java 8 i nowszych

public interface MyInterface {
 
    void abstractMethod();        // metoda abstrakcyjna
 
    default void defaultMethod() { 
        System.out.println("To jest metoda domyślna"); 
    }
 
    static void staticMethod() {
        System.out.println("To jest metoda statyczna");
    }
}
→ Slide 18

Interfejsy funkcyjne

→ Slide 19

Interfejs Comparable

interface Comparable<T> {
    int compareTo(T o);
}
→ Slide 20

Klasa abstrakcyjna vs Interfejs

Cecha Klasa abstrakcyjna Interfejs
Metody metody abstrakcyjne i konkretne metody abstrakcyjne (od Java 8 także default i static)
Deklaracja abstract class interface
Konstruktor może mieć konstruktor (nie można instancjonować) brak
Pola dowolne domyślnie public static final
Dziedziczenie brak wielodziedziczenia można implementować wiele interfejsów
→ Slide 21

Rzutowanie w górę (Upcasting)

Animal a = new Dog();
→ Slide 22

Rzutowanie w dół (Downcasting)

Rose r = (Rose) flower;
→ Slide 23

Przykład

class Flower {}
class Rose extends Flower {
  public void bloom() {
    System.out.println("Rose is blooming");
  }
}
 
public class Main {
  public static void main(String[] args) {
    Flower f = new Rose();
    Rose r = (Rose) f;     // downcasting
    r.bloom();          // dostęp do metody specyficznej dla Rose
  }
}
→ Slide 24

Operator instanceof

Flower f = new Rose();
if (f instanceof Rose) { 
    Rose r = (Rose) f;         /* bezpieczne */ 
    r.bloom();
}
→ Slide 25

Klasy anonimowe

Klasa anonimowa to klasa definiowana „w locie”, mająca dokładnie jedną instancję. Klasa anonimowa jest zawsze klasą wewnętrzną.

public interface Animal {
    void sound();
}
public class Main {
    public static void main(String[] args) {
        Animal cat = new Animal() {
            @Override
            public void sound() {
                System.out.println("Miauuu...");
            }
        };
        cat.sound();
    }
}
→ Slide 26

Klasy anonimowe - uwaga

→ Slide 27

Klasy anonimowe i lambda

Przykład klasy anonimowej:

Runnable r = new Runnable() {
    @Override 
    public void run() { System.out.println("Uruchomiono"); }
};
 
// Lambda (krótsze, Java 8+)
Runnable r2 = () -> System.out.println("Uruchomiono");

Uwaga: zmienne używane w klasach anonimowych muszą być final lub efektywnie finalne (Java 8+).

→ Slide 28

Typy parametryczne ograniczone

public class Complex<T extends Number> {
    private T real;
    private T imaginary;
 
    public Complex<T> add(Complex<T> other) {
        double realPart = this.real.doubleValue() + other.real.doubleValue();
        // ...
    }
}
→ Slide 29

Argumenty wieloznaczne (?)

class Complex<T extends Number> {
    private T real;
    private T imaginary;
 
    public T getReal() { return real; }
 
   public Complex<T> add(Complex<? extends Number> other) {
        double realPart = (this.real.doubleValue() + other.getReal().doubleValue();
        // ...
    }
}
→ Slide 30

Najczęstsze błędy OOP:

→ Slide 31

Zasady SOLID:

→ Slide 32

Ćwiczenie:

Rozbuduj klasę Complex z poprzednich:

Utwórz klasę potomną PolarComplex rozszerzającą klasę Complex o następujące elementy:

→ Slide 33

Zadanie 3. Klasy Fraction i MixedFraction

Rozbuduj klasę ułamków Fraction z poprzedniego zadania (zob. zadanie 2)

Wymagania:

Zaimplementuj klasę MixedFraction, która rozszerza Fraction:

Utwórz interfejs Randomizable z metodą:

Zaimplementuj Randomizable dla Fraction i MixedFraction.

Napisz program testowy: