Dziedziczenie pozwala klasie potomnej (podklasie, subclass) odziedziczyć pola i metody klasy bazowej (nadklasy, superclass).
Korzyści:
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 }
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; } }
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 { }
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
@Override jest zalecane, aby wskazać, że metoda jest przesłonięciem, co pomaga uniknąć błędów.class Animal { void sound() { System.out.println("Jakiś dźwięk"); } }
class Dog extends Animal { @Override void sound() { System.out.println("Hau hau"); } }
class Parent { void method() { } } class Child extends Parent { void method(int a) { } void method(int a, int b) { } }
super(args) pozwala wywołać jawnie konstruktor klasy bazowejsuper() wyklucza this() - instrukcja musi być pierwszą instrukcją w konstruktorzepublic 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"); } }
super.method() - wywołanie metody nadklasysuper(args) - wywołanie konstruktora nadklasysuper.field - dostęp do pól nadklasyclass 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."); } }
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:
toString() - zwraca reprezentację tekstową obiektu (domyślnie zawiera nazwę klasy i hash code),equals(Object obj) - porównuje obiekty pod względem zawartości (domyślnie porównuje referencje)hashCode() - zwraca hash code obiektu (domyślnie oparty na adresie pamięci),getClass() - zwraca obiekt klasy reprezentujący klasę obiektuclone() - tworzy kopię obiektu (wymaga implementacji interfejsu Cloneable)finalize() - metoda wywoływana przez garbage collector przed usunięciem obiektu (niezalecana do użycia)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"); } }
Number jest klasą abstrakcyjną, która reprezentuje liczby. Dziedziczą po niej klasy takie jak Integer, Double, Float, BigDecimal, itp.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(); }
public abstract) i stałe pola (domyślnie public static final)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"; } }
default) z implementacją oraz metody statycznepublic 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"); } }
Runnable, Callable, Comparator, Comparable, Function, Predicate.compareTo, która służy do porównywania obiektów tego samego typu.Comparable mogą być sortowane przez Collections.sort() i Arrays.sort() interface Comparable<T> { int compareTo(T o); }
this < othis == othis > o| 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 |
Animal a = new Dog();
Rose r = (Rose) flower;
ClassCastException w czasie wykonywania, jeśli obiekt nie jest instancją docelowego typu.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 } }
instanceof sprawdza, czy obiekt jest instancją danego typu lub jego podtypu (true lub false).ClassCastException.Flower f = new Rose(); if (f instanceof Rose) { Rose r = (Rose) f; /* bezpieczne */ r.bloom(); }
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(); } }
final lub być efektywnie finalna (Java 8+), czyli jej wartość jest niezmiennaPrzykł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+).
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(); // ... } }
<T extends ClassA & InterfaceB>? to symbol używany w generykach do reprezentowania nieznanego typu (wildcard).List<?>, List<? extends Number>, List<? super Integer> 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(); // ... } }
@Override przy przesłanianiu i brak instanceof przy rzutowaniach
Rozbuduj klasę Complex z poprzednich:
Number)ArithemeticOperations<T> uogólniający operacje arytmetyczne add(), substract(), divide(), multiply()equals() klasy Object dla liczb zespolonych
Utwórz klasę potomną PolarComplex rozszerzającą klasę Complex o następujące elementy:
r oraz kąt phitoString() tak, by pokazywała postać biegunową, np. r=2.00, phi=1.57
Rozbuduj klasę ułamków Fraction z poprzedniego zadania (zob. zadanie 2)
Wymagania:
Number i implementować metody abstrakcyjne z tej klasyComparableequals(Object) tak, aby ułamki o tej samej wartości były równe (np. 1/2 i 2/4).
Zaimplementuj klasę MixedFraction, która rozszerza Fraction:
-3 1/3.toString(), aby wypisywała wartość w postaci mieszanej.
Utwórz interfejs Randomizable z metodą:
generate(Random r) - wypełnia obiekt losową zawartością i zwraca referencję this.
Zaimplementuj Randomizable dla Fraction i MixedFraction.
Napisz program testowy:
Fraction i 100 obiektów MixedFraction i umieść je w tablicyCollections.sort() lub Arrays.sort()),