Learning Log #3: Tydzień 12 Zajavka – interfejsy, klasy abstrakcyjne i koniec podstaw Java

with Brak komentarzy
interfejsy Java.

Learning Log #3: Tydzień 12 Zajavka – interfejsy, klasy abstrakcyjne i koniec podstaw Java

Siedemnasty tydzień od startu z kursem Zajavka. Oficjalnie tydzień 12 – ostatni tydzień podstawowej części kursu.

Kawa się parowała. Słuchawki na uszach. Dziecko spokojne. I ja, patrząc na ekran z napisem „Gratulacje! Udało Ci się ukończyć kurs podstawowy!” czułam… ulgę. I dumę. I lekki strach „co dalej?”.

Interfejsy w Java brzmiały dla mnie przez długi czas jak jakaś magiczna zaawansowana rzecz. Dziś wiem, że to jeden z najpotężniejszych narzędzi w OOP. I pokażę Wam dlaczego.

Gdzie jestem w kursie Java?

Oficjalnie: Tydzień 12/12 kursu „Java w 12 tygodni Zajavka” – koniec podstaw ✓

Realnie: 17 tygodni od startu (czyli ~4 miesiące kalendarzowe)

Co dalej: Zajavka ma część zaawansowaną – Spring, bazy danych, REST API. Decyduję czy kontynuować czy przejść na JavaScript.

Czas nauki w tym tygodniu: 10 godzin (wracam do formy!)

Czym są interfejsy w Java? (w końcu rozumiem!)

Analogia która mi pomogła – umowa o pracę:

Wyobraź sobie że zatrudniasz pracownika. Nie obchodzi Cię:

  • Jak dokładnie wykonuje pracę
  • Jakie narzędzia używa
  • Jaki ma proces myślowy

Obchodzi Cię tylko: czy potrafi zrobić to co potrzebujesz.

Interfejs w Java to właśnie taka „umowa” – deklaracja „potrafię to i to” bez szczegółów „jak”.

Przykład z życia – Payment:

java

`// INTERFEJS = umowa public interface Payment { boolean processPayment(double amount); String getPaymentStatus(); }

// IMPLEMENTACJA 1 - Karta kredytowa public class CreditCardPayment implements Payment { private String cardNumber;

public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}

@Override
public boolean processPayment(double amount) {
System.out.println("Processing " + amount + " PLN via credit card");
// Tutaj logika połączenia z operatorem kart
return true;
}

@Override
public String getPaymentStatus() {
return "Credit card payment successful";
}

}
// IMPLEMENTACJA 2 - BLIK public class BlikPayment implements Payment { private String phoneNumber;

public BlikPayment(String phoneNumber) {
this.phoneNumber = phoneNumber;
}

@Override
public boolean processPayment(double amount) {
System.out.println("Processing " + amount + " PLN via BLIK");
// Tutaj logika BLIK
return true;
}

@Override
public String getPaymentStatus() {
return "BLIK payment successful";
}

}

// UŻYCIE - najważniejsze! public class PaymentProcessor {

public void executePayment(Payment payment, double amount) {
// Nie wiem CZY to karta, BLIK czy coś innego
// Wiem tylko że payment POTRAFI processPayment()
if (payment.processPayment(amount)) {
System.out.println(payment.getPaymentStatus());
}
}


}

// W praktyce: PaymentProcessor processor = new PaymentProcessor();

Payment creditCard = new CreditCardPayment("1234-5678"); Payment blik = new BlikPayment("123456789");

processor.executePayment(creditCard, 100.50); processor.executePayment(blik, 200.00);`

Co mnie zaskoczyło?

PaymentProcessor nie wie, czy dostaje kartę czy BLIK. Wie tylko że dostaje coś co implementuje Payment. I to wystarczy!

To jest cała magia interfejsów – loose coupling, flexibility, extensibility.

Dlaczego interfejsy są lepsze niż klasy abstrakcyjne?

To było moje największe „aha!” tego tygodnia.

Klasa abstrakcyjna:

java

`public abstract class Animal { protected String name;

public Animal(String name) {
this.name = name;
}

public abstract void makeSound();

public void sleep() { // Konkretna implementacja
System.out.println(name + " is sleeping");
}


}

public class Dog extends Animal { public Dog(String name) { super(name); }

@Override
public void makeSound() {
System.out.println("Woof!");
}


}`

Problem: Dog może dziedziczyć tylko z JEDNEJ klasy. Co jeśli Dog ma być też Trainable? Nie można.

Interfejsy – wiele implementacji!

java

`public interface Animal { void makeSound(); }

public interface Trainable { void train(String command); boolean isTraned(); }

public interface Playful { void play(); }

// Jedna klasa, WIELE interfejsów! public class Dog implements Animal, Trainable, Playful { private String name; private boolean trained = false;

@Override
public void makeSound() {
System.out.println("Woof!");
}

@Override
public void train(String command) {
System.out.println("Training: " + command);
trained = true;
}

@Override
public boolean isTrained() {
return trained;
}

@Override
public void play() {
System.out.println("Playing fetch!");
}

}`

Java nie ma multiple inheritance (dziedziczenia wielokrotnego) ale ma multiple implementation (implementacji wielokrotnych)!

To rozwiązuje problem „co jeśli potrzebuję cech z wielu źródeł”.

Kiedy używać czego? (moja ściąga)

Użyj INTERFEJSU gdy…Użyj KLASY ABSTRAKCYJNEJ gdy…
Określasz „co klasa potrafi”Definiujesz „czym klasa jest”
Potrzebujesz wielu źródeł zachowańMasz wspólny kod do współdzielenia
Chcesz loose couplingMasz silną relację hierarchiczną
Przykład: Flyable, Swimmable, PayablePrzykład: Animal, Vehicle, Shape

Praktyczna zasada: Domyślnie używaj interfejsów. Klasy abstrakcyjne tylko, gdy naprawdę potrzebujesz współdzielić implementację.

Mój projekt – Therapy Management System

Postanowiłam stworzyć coś użytecznego – system zarządzania terapiami mojego dziecka.

Design z interfejsami:

java

`// INTERFEJSY - umowy public interface Schedulable { LocalDateTime getDateTime(); int getDuration(); boolean reschedule(LocalDateTime newTime); }

public interface Billable { double getCost(); boolean isPaid(); void markAsPaid(); }

public interface Reportable { String generateReport(); Map<String, Object> getStatistics(); }

// IMPLEMENTACJA - terapia ma wszystkie 3 cechy public class TherapySession implements Schedulable, Billable, Reportable { private String therapyType; // "SI", "Logopeda", "Pedagog" private LocalDateTime dateTime; private int durationMinutes; private double cost; private boolean paid; private String notes;

public TherapySession(String type, LocalDateTime dateTime,
int duration, double cost) {
this.therapyType = type;
this.dateTime = dateTime;
this.durationMinutes = duration;
this.cost = cost;
this.paid = false;
}

// Schedulable methods
@Override
public LocalDateTime getDateTime() {
return dateTime;
}

@Override
public int getDuration() {
return durationMinutes;
}

@Override
public boolean reschedule(LocalDateTime newTime) {
this.dateTime = newTime;
System.out.println("Rescheduled to: " + newTime);
return true;
}

// Billable methods
@Override
public double getCost() {
return cost;
}

@Override
public boolean isPaid() {
return paid;
}

@Override
public void markAsPaid() {
this.paid = true;
System.out.println("Marked as paid: " + cost + " PLN");
}

// Reportable methods
@Override
public String generateReport() {
return String.format("Therapy: %s | Date: %s | Duration: %d min | Cost: %.2f PLN | Paid: %s",
therapyType, dateTime, durationMinutes, cost, paid ? "Yes" : "No");
}

@Override
public Map<String, Object> getStatistics() {
Map<String, Object> stats = new HashMap<>();
stats.put("type", therapyType);
stats.put("duration", durationMinutes);
stats.put("cost", cost);
stats.put("paid", paid);
return stats;
}

}

// MANAGEMENT CLASSES - pracują z interfejsami! public class BillingManager {

public double calculateTotalUnpaid(List<Billable> items) {
return items.stream()
.filter(item -> !item.isPaid())
.mapToDouble(Billable::getCost)
.sum();
}

public void processPayments(List<Billable> items) {
items.stream()
.filter(item -> !item.isPaid())
.forEach(item -> {
item.markAsPaid();
});
}

}

public class ScheduleManager {

public List<Schedulable> getUpcoming(List<Schedulable> items) {
LocalDateTime now = LocalDateTime.now();
return items.stream()
.filter(item -> item.getDateTime().isAfter(now))
.sorted(Comparator.comparing(Schedulable::getDateTime))
.toList();
}

}

public class ReportGenerator {

public void generateMonthlyReport(List<Reportable> items) {
System.out.println("=== MONTHLY REPORT ===");
items.forEach(item -> {
System.out.println(item.generateReport());
});
}

}`

Użycie w praktyce:

java

`public class Main { public static void main(String[] args) { // Tworzę sesje terapii List<TherapySession> sessions = new ArrayList<>();

sessions.add(new TherapySession("SI",
LocalDateTime.of(2026, 3, 20, 14, 0), 60, 75.00));
sessions.add(new TherapySession("Logopeda",
LocalDateTime.of(2026, 3, 22, 10, 0), 45, 65.00));
sessions.add(new TherapySession("Pedagog",
LocalDateTime.of(2026, 3, 25, 15, 0), 60, 60.00));

// Billing - obsługuje Billable
BillingManager billing = new BillingManager();
double unpaid = billing.calculateTotalUnpaid(
new ArrayList<>(sessions)); // TherapySession implements Billable!
System.out.println("Total unpaid: " + unpaid + " PLN");

// Schedule - obsługuje Schedulable
ScheduleManager schedule = new ScheduleManager();
List<Schedulable> upcoming = schedule.getUpcoming(
new ArrayList<>(sessions)); // TherapySession implements Schedulable!
System.out.println("Upcoming sessions: " + upcoming.size());

// Reports - obsługuje Reportable
ReportGenerator reports = new ReportGenerator();
reports.generateMonthlyReport(
new ArrayList<>(sessions)); // TherapySession implements Reportable!
}

}


**Output:**

Total unpaid: 200.00 PLN Upcoming sessions: 3 === MONTHLY REPORT === Therapy: SI | Date: 2026-03-20T14:00 | Duration: 60 min | Cost: 75.00 PLN | Paid: No Therapy: Logopeda | Date: 2026-03-22T10:00 | Duration: 45 min | Cost: 65.00 PLN | Paid: No Therapy: Pedagog | Date: 2026-03-25T15:00 | Duration: 60 min | Cost: 60.00 PLN | Paid: No`

Dlaczego to jest piękne?

BillingManager nie wie, że pracuje z TherapySession. Wie tylko, że dostaje Billable.

Jutro mogę dodać DoctorVisit implements Billable i BillingManager będzie działać BEZ ZMIAN!

To jest siła interfejsów – rozszerzalność bez modyfikacji istniejącego kodu.

Default methods w interfejsach (Java 8+)

To mnie totalnie zaskoczyło – interfejsy mogą mieć implementację!

java

`public interface Reportable { String generateReport();

// DEFAULT METHOD - konkretna implementacja!
default void printReport() {
System.out.println("=== REPORT ===");
System.out.println(generateReport());
System.out.println("==============");
}

default String getShortSummary() {
String full = generateReport();
return full.substring(0, Math.min(50, full.length())) + "...";
}

}`

Teraz każda klasa implementująca Reportable automatycznie dostaje printReport() i getShortSummary()!

Kiedy używać:

  • Gdy większość implementacji będzie miała tą samą logikę
  • Gdy chcesz dodać metodę do interfejsu bez łamania istniejącego kodu

Typowe błędy które popełniłam

1. Zbyt duże interfejsy (zanieczyszczenie interfejsów)

Błąd:

java

public interface TherapyManagement { void schedule(); void reschedule(); void cancel(); double getCost(); void markAsPaid(); String generateReport(); void sendReminder(); void exportToCalendar(); // ... 20 metod później }

Lekcja: To narusza ISP (Interface Segregation Principle – Zasada Segregacji Interfejsów ). Lepiej wiele małych interfejsów niż jeden wielki.

Poprawka: Rozdziel na Schedulable, Billable, Reportable, Notifiable.

2. Interfejs z jedną metodą bez sensu

Błąd:

java

public interface CanExist { boolean exists(); }

Lekcja: Nie każda metoda potrzebuje interfejsu. Czasem po prostu metoda w klasie wystarczy.

3. Mieszanie abstrakcji

Błąd:

java

public interface Payment { boolean processPayment(double amount); void setCardNumber(String card); // Co jeśli to BLIK? Nie ma karty! }

Lekcja: Interfejs powinien opisywać zachowanie wspólne dla WSZYSTKICH implementacji.

Functional Interfaces – bonus

Odkryłam że interfejs z JEDNĄ metodą abstrakcyjną to „functional interface” i może być użyty z lambda!

java

`@FunctionalInterface public interface TherapyCostCalculator { double calculate(int sessions, double pricePerSession); }

// Użycie z lambdą: TherapyCostCalculator standard = (sessions, price) -> sessions * price; TherapyCostCalculator withDiscount = (sessions, price) -> sessions * price * 0.9; // 10% discount

System.out.println("Standard: " + standard.calculate(8, 75)); // 600.0 System.out.println("Discount: " + withDiscount.calculate(8, 75)); // 540.0`

To otwiera drogę do programowania funkcyjnego w Java!

Koniec podstaw Zajavka – co dalej?

Tydzień 12 = koniec fundamentów. Przeszłam przez:

  • Podstawy składni Java
  • OOP (klasy, obiekty, dziedziczenie)
  • Agregacja i kompozycja
  • Interfejsy i klasy abstrakcyjne

Co dalej – moje opcje:

Opcja 1: Zajavka – poziom zaawansowany

  • Spring Framework
  • Hibernate / JPA
  • REST APIs
  • Microservices

Plusy: Kontynuacja, naturalny postęp, solidna backend-dev ścieżka Minusy: Jeszcze więcej Java, może za ciężkie przy dzieciach

Opcja 2: JavaScript

  • Naturalny krok dla freelancerki WordPress
  • Synergia z moją pracą
  • Frontend interaktywność

Plusy: Praktyczne dla mojej kariery, lżejsze od Springa, już mam podstawy JS Minusy: Restart w kolejnym języku, powtarzanie podstaw

Opcja 3: Konsolidacja + Budowa

  • Zatrzymać się na podstawach Java
  • Zbudować 2-3 solidne projekty
  • Pokazać portfolio

Plusy: Solidne fundamenty, działający kod Minusy: Może za mało dla ryneku pracy

Moja decyzja: Myślę, że… JavaScript. Powody:

  1. Synergia z WordPress (mogę od razu używać w pracy!)
  2. Łatwiejszy balans z dziećmi (dużo mniej ciężki niż Spring)
  3. Frontend umiejętności są wartościowe
  4. Zawsze mogę wrócić do Java później

Podsumowanie nauki Java – 17 tygodni

Co osiągnęłam:
✅ Podstawy składni Java – solidnie
✅ OOP – rozumiem i używam
✅ Agregacja/kompozycja – odhaczone
✅ Interfejsy – w końcu rozumiem!
✅ 2 projekty robocze (Family system, Therapy Management)

Statystyki:

  • 17 tygodni (zamiast 12)
  • ~120 godzin nauki całościowo
  • ~7h tygodniowo średnio
  • 2 pełne projekty
  • Setki linii kodu

Czy było warto? TAK. Java nauczyła mnie:

  • Myślenia obiektowo
  • Projektowania systemów
  • Że mogę uczyć się programowania przy dzieciach
  • Że 1h dziennie wystarczy

Następny krok: Learning Log #4 będzie o… JavaScript albo o PHP (?!) ! Czekać na dalsze informacje.


PS: Też kończycie kurs/etap nauki? Co dalej planujesz? Zostaw komentarz – ciekawa jestem Waszych ścieżek. A jeśli macie pytania o interfejsy w Java – pytajcie, teraz gdy w końcu rozumiem chętnie pomogę!

Zostaw Komentarz