Wykład 10
W Javie można podpiąć (implementować) kilka interfejsów do jednej klasy. Jest to jedna z fundamentalnych cech języka Java, która pozwala na wielokrotną implementację interfejsów, co umożliwia wielokrotne dziedziczenie zachowań. W przeciwieństwie do dziedziczenia klas, gdzie Java pozwala na dziedziczenie tylko z jednej klasy bazowej, klasa może implementować dowolną liczbę interfejsów.
public interface InterfaceA {
void methodA();
}
public interface InterfaceB {
void methodB();
}
public class MyClass implements InterfaceA, InterfaceB {
@Override
public void methodA() {
// Implementacja metody z InterfaceA
}
@Override
public void methodB() {
// Implementacja metody z InterfaceB
}
}Jeśli dwie metody abstrakcyjne w różnych interfejsach mają tę samą sygnaturę, klasa implementująca te interfejsy musi dostarczyć tylko jedną implementację tej metody. Język Java traktuje je jako jedną i tę samą metodę.
Jeśli dwa interfejsy definiują metody domyślne (default) o tej samej sygnaturze, klasa implementująca te interfejsy musi przesłonić tę metodę, aby rozwiązać konflikt.
public interface InterfaceA {
default void metoda() {
System.out.println("InterfaceA metoda");
}
}
public interface InterfaceB {
default void metoda() {
System.out.println("InterfaceB metoda");
}
}
public class MyClass implements InterfaceA, InterfaceB {
@Override
public void metoda() {
InterfaceA.super.metoda(); // Wywołanie konkretnej metody domyślnej
// lub własna implementacja
}
}interface MyInterfaceA {
void methodA();
}
// Interfejs dziedziczący
interface MyInterfaceB extends MyInterfaceA {
void methodB();
}
// Klasa implementująca InterfejsB
class Klasa implements MyInterfaceB {
public void methodA() {
// Implementacja metody z InterfejsA
}
public void methodB() {
// Implementacja metody z InterfejsB
}
}Rzutowanie obiektów na interfejs w Javie to proces, w którym obiekt klasy, która implementuje dany interfejs, jest traktowany jako instancja tego interfejsu. Jest to często używane, gdy chcemy skorzystać z metod określonych w interfejsie, nie zwracając uwagi na konkretną klasę obiektu. Rzutowanie jest szczególnie ważne w kontekście polimorfizmu, gdzie obiekty różnych klas mogą być traktowane jako instancje wspólnego interfejsu.
sort z klasy Arraysimport java.util.Arrays;
public class TestArray {
public static void main(String[] args) {
int[] intArray = {5, 2, 8, -3, 1};
Arrays.sort(intArray);
System.out.println(Arrays.toString(intArray));
double[] doubleArray = {3.14, -1.59, 2.65, 3.58};
Arrays.sort(doubleArray);
System.out.println(Arrays.toString(doubleArray));
String[] stringArray = {"Banana", "apple", "Cherry", "Date"};
Arrays.sort(stringArray);
System.out.println(Arrays.toString(stringArray));
}
}Interfejs Comparable w Javie jest używany do definiowania naturalnego porządku obiektów danej klasy. Kiedy klasa implementuje interfejs Comparable, oznacza to, że obiekty tej klasy mogą być porównywane ze sobą, co jest szczególnie użyteczne do sortowania.
Na wykładzie będzie omawiana generyczna wersja Comparable<T>.
Interfejs Comparable zawiera jedną metodę, którą należy zaimplementować:
Metoda compareTo(T o) zwraca:
o.o.Metoda sort z klasy Arrays może być użyta do sortowania tablic obiektów, które implementują interfejs Comparable. Sortowanie jest wtedy przeprowadzane zgodnie z naturalnym porządkiem określonym przez metodę compareTo.
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person other) {
return this.age - other.age;
}
@Override
public String toString() {
return name + " - " + age;
}
// Gettery, settery, inne metody...
}Comparable jest ważne?Definiowanie Naturalnego Porządku: Implementacja Comparable pozwala klasom określić, co znaczy, że jeden obiekt jest „większy”, „mniejszy” lub „równy” innemu.
Ułatwia Sortowanie i Porównywanie: Dzięki Comparable, możemy używać metod takich jak Arrays.sort lub kolekcji, które automatycznie sortują elementy (np. TreeSet), bez konieczności określania dodatkowego komparatora.
Większa Elastyczność i Czytelność Kodu: Implementacja Comparable w klasie sprawia, że porównanie i sortowanie obiektów tej klasy staje się bardziej intuicyjne i zintegrowane z naturalnymi operacjami Javy.
intpublic class TestMyNumber {
public static void main(String[] args) {
MyNumber[] numbers = {
new MyNumber(5),
new MyNumber(2),
new MyNumber(8),
new MyNumber(-3),
new MyNumber(1)
};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));
}
}
class MyNumber implements Comparable<MyNumber> {
private int value;
public MyNumber(int value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
@Override
public int compareTo(MyNumber other) {
//
}
}@Override
public int compareTo(MyNumber o) {
if (this.value < o.value) return -1;
if (this.value > o.value) return 1;
return 0;
}Integer.compareObjects.compare: W Javie 7 i nowszych, można użyć Objects.compare w połączeniu z Comparator:@Override
public int compareTo(MyNumber o) {
return Objects.compare(this.value, o.value, Integer::compare);
}doubleimport java.util.Arrays;
public class TestMyDouble {
public static void main(String[] args) {
MyDouble[] numbers = {
new MyDouble(5),
new MyDouble(2),
new MyDouble(8),
new MyDouble(-3),
new MyDouble(1)
};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));
}
}
class MyDouble implements Comparable<MyDouble> {
private double value;
public MyDouble(double value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
@Override
public int compareTo(MyDouble o) {
return Double.compare(this.value, o.value);
}
}Stringimport java.util.Arrays;
public class TestMyString {
public static void main(String[] args) {
MyString[] strings = new MyString[3];
strings[0] = new MyString("Hello");
strings[1] = new MyString("World");
strings[2] = new MyString("Java");
System.out.println(Arrays.toString(strings));
Arrays.sort(strings);
System.out.println(Arrays.toString(strings));
}
}
class MyString implements Comparable<MyString> {
private String value;
public MyString(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
@Override
public int compareTo(MyString o) {
return value.compareTo(o.value);
}
}Inne sposoby:
equals i compareToW Javie, gdy implementujesz metody equals i compareTo w konkretnej klasie, istnieje ważna zasada, która powinna być zachowana, znana jako zgodność equals i compareTo. Zgodność ta oznacza, że wynik metody equals powinien być zgodny z wynikiem metody compareTo.
compareTo zwraca 0 (co wskazuje, że obiekty są równe pod względem porządku), to equals powinno również zwrócić true, sugerując, że obiekty są równoważne.equals zwraca true (wskazując, że obiekty są równoważne), to compareTo powinno zwrócić 0, wskazując, że są one równe pod względem porządku.equals.public class Person implements Comparable<Person> {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int compareTo(Person other) {
int nameComparison = name.compareTo(other.name);
if (nameComparison != 0) {
return nameComparison;
}
return Integer.compare(age, other.age);
}
}Uwaga! null nie należy do żadnej klasy. obj.compareTo(null) wyrzuca wyjątek, ale obj.equals(null) zwraca false.
Większość implementujących interfejs Comparable klas API Javy honoruje tę zasadę, ale jednym z ważnych wyjątków jest klasa BigDecimal.
import java.math.BigDecimal;
import java.util.Arrays;
public class TestBigDecimal {
public static void main(String[] args) {
BigDecimal[] numbers = new BigDecimal[3];
numbers[0] = new BigDecimal("1.0");
numbers[1] = new BigDecimal("1.00");
numbers[2] = new BigDecimal("1.000");
System.out.println(Arrays.toString(numbers));
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));
}
}Comparable<T> a dziedziczenieJeśli klasa bazowa X implementuje interfejs Comparable<X>, to klasa pochodna Y dziedzicząca po X nie może implementować interfejsu Comparable<Y>.
Można dodać w klasie pochodnej Y implementację interfejsu Comparable<X>.
Wyrzucanie ClassCastException, jeśli typy są różne
public class Employee implements Comparable<Employee> {
private String name;
private int salary;
// Konstruktory, gettery, settery itp.
@Override
public int compareTo(Employee other) {
// Porównanie na podstawie nazwiska
int nameComparison = this.name.compareTo(other.name);
if (nameComparison != 0) {
return nameComparison;
}
// Porównanie na podstawie wynagrodzenia
return Integer.compare(this.salary, other.salary);
}
}
public class Manager extends Employee {
private int bonus;
// Konstruktory, gettery, settery itp.
@Override
public int compareTo(Employee other) {
if (other.getClass() != Manager.class) {
throw new ClassCastException("Nie można porównać Managera z innym typem Employee");
}
Manager otherManager = (Manager) other;
int baseComparison = super.compareTo(otherManager);
if (baseComparison != 0) {
return baseComparison;
}
// Porównanie na podstawie bonusu
return Integer.compare(this.bonus, otherManager.bonus);
}
}Obiekty klasy pochodnej są uzupełniane o dodatkowe pole.
instanceof używane jest do zachowania hierarchii w porządku.
public class Employee implements Comparable<Employee> {
private String name;
private int salary;
// Konstruktory, gettery, settery itp.
@Override
public int compareTo(Employee other) {
// Porównanie na podstawie nazwiska
int nameComparison = this.name.compareTo(other.name);
if (nameComparison != 0) {
return nameComparison;
}
// Porównanie na podstawie wynagrodzenia
return Integer.compare(this.salary, other.salary);
}
}
public class Manager extends Employee {
private int bonus;
// Konstruktory, gettery, settery itp.
@Override
public int compareTo(Employee other) {
if (other instanceof Manager) {
Manager otherManager = (Manager) other;
int baseComparison = super.compareTo(otherManager);
if (baseComparison != 0) {
return baseComparison;
}
// Porównanie na podstawie bonusu
return Integer.compare(this.bonus, otherManager.bonus);
}
return super.compareTo(other);
}
}