SEN1002 · 4. hafta · Polymorphism II: Abstract Classes and Interfaces

Abstract sınıflar ve arayüzler (interface)

abstract metot, interface, default metot, birden fazla arayüz ve Comparable ile sıralama. Derleme hatası sorularının ana kaynağı.

abstract classabstract methodconcrete classinterfaceimplementsdefault methodstatic interface methodconstantmultiple interfacesComparablecompareToArrays.sort

Geçen hafta Shape sınıfına area() yazıp 0 döndürdük. Ama “genel bir şekil”in alanı anlamsız; o metodun tek görevi alt sınıflara “sen bunu yazacaksın” demekti. Abstract sınıflar ve arayüzler (interface) bu niyeti dile döker: derleyici, sözleşmeyi yerine getirmeyen sınıfı derlemez. Sınavda bu yüzden çok “find the compile error” sorusu gelir, ayrıca Comparable ile “sort this array” tipi yazma soruları.

Altın kurallar:

  1. Abstract sınıftan new yapılmaz ama tip olarak kullanılır.
  2. Abstract metodu olan sınıf abstract olmak zorunda. Somut alt sınıf hepsini yazmak zorunda.
  3. Arayüz metotları public’tir. Implement ederken public yazmayı unutma.
  4. Tek extends, çok implements.
  5. compareTo: negatif, sıfır, pozitif. Arrays.sort bunu kullanır.

Abstract sınıf

abstract anahtar kelimesiyle işaretlenen sınıf eksik bir kalıptır. İçinde gövdesiz (abstract) metotlar olabilir. Normal sınıf gibi alanları, constructor’ı ve gövdeli metotları da olabilir.

abstract class Employee {
    private final String name;

    Employee(String name) {                 // abstract sınıfın da constructor'ı var
        this.name = name;
        System.out.println("Employee(" + name + ")");
    }
    String getName() { return name; }

    abstract double earnings();             // gövde yok, noktalı virgül

    @Override
    public String toString() { return getName() + ": " + earnings(); }
}

class Salaried extends Employee {
    private final double monthly;
    Salaried(String n, double m) { super(n); monthly = m; }
    @Override double earnings() { return monthly; }
}

class Hourly extends Employee {
    private final double rate;
    private final int hours;
    Hourly(String n, double r, int h) { super(n); rate = r; hours = h; }
    @Override double earnings() {
        if (hours <= 40) return rate * hours;
        return rate * 40 + rate * 1.5 * (hours - 40);   // fazla mesai
    }
}
Employee[] staff = { new Salaried("Ada", 3000), new Hourly("Bora", 100, 45) };
for (Employee e : staff) System.out.println(e);
Employee(Ada)
Employee(Bora)
Ada: 3000.0
Bora: 4750.0

new Employee("X") yazsaydın: Employee is abstract; cannot be instantiated. Ama Employee[] dizisi ve Employee e değişkeni sorunsuz; abstract sınıf polimorfizm için ortak tip olarak kullanılır.

Abstract ile ilgili kurallar

DurumSonuç
new AbstractSinif()Derleme hatası
Sınıfta abstract metot var ama sınıf abstract değilDerleme hatası
Somut alt sınıf bir abstract metodu yazmadıDerleme hatası: is not abstract and does not override abstract method
Alt sınıf da abstractSerbest, metotları yazmak zorunda değil; sorumluluk bir sonraki somut sınıfa geçer
Abstract sınıfta constructorVar olabilir, super(...) ile çağrılır
abstract + private / static / final metotDerleme hatası (override edilemeyen metot gövdesiz kalamaz)

Arayüz (interface)

Arayüz, bir sınıfın “neler yapabildiğini” söyleyen sözleşmedir. Kalıtımdaki “is-a” ilişkisinden daha gevşek: “can-do” ilişkisi. Birbiriyle hiç ilgisi olmayan sınıflar (fatura, çalışan, kira) aynı Payable arayüzünü uygulayabilir.

Arayüzde yazılanların gizli anlamları:

YazdığınDerleyicinin anladığı
double amount();public abstract double amount();
double TAX = 0.2;public static final double TAX = 0.2; (sabit)
default double afterTax() { ... }Gövdeli metot, sınıf isterse override eder (Java 8+)
static Payable fixed(double v) { ... }Sadece Payable.fixed(...) ile çağrılır
interface Payable {
    double TAX = 0.2;
    double amount();
    default double afterTax() { return amount() * (1 - TAX); }
}

class Invoice implements Payable {
    private final int qty;
    private final double price;
    Invoice(int q, double p) { qty = q; price = p; }
    public double amount() { return qty * price; }   // public ŞART
}

Payable p = new Invoice(3, 50);
System.out.println(p.amount() + " " + p.afterTax());
System.out.println(Payable.TAX);
150.0 120.0
0.2

TUZAK · public unutmak. Arayüzde public yazmasan da metot public’tir. Sınıfta yazmazsan package-private olur ve erişim daralır: attempting to assign weaker access privileges; was public. En sık görülen interface derleme hatası budur.

Arayüz sabitini değiştirmeye çalışmak da hata verir: Payable.TAX = 0.3; → cannot assign a value to static final variable TAX.

Birden fazla arayüz

Bir sınıf tek bir sınıftan türeyebilir ama istediği kadar arayüz uygulayabilir. Yazım sırası önemli: önce extends, sonra implements.

class Robot extends Machine implements Payable, Comparable<Robot> { ... }

İki arayüz aynı imzalı default metodu getirirse Java hangisini seçeceğini bilemez ve derleme hatası verir. Çözüm: sınıf metodu override eder, isterse ArayuzAdi.super.metot() ile birini (ya da ikisini) çağırır.

interface Flyer   { default String move() { return "fly"; } }
interface Swimmer { default String move() { return "swim"; } }

class Duck implements Flyer, Swimmer {
    public String move() { return Flyer.super.move() + "+" + Swimmer.super.move(); }
}
System.out.println(new Duck().move());
fly+swim

Override olmadan class Duck implements Flyer, Swimmer { } yazarsan: types Flyer and Swimmer are incompatible; class Duck inherits unrelated defaults for move() from types Flyer and Swimmer.

Abstract sınıf mı, arayüz mü?

Abstract sınıfInterface
Kaç tanesinden türeyebilir?Bir (extends)Çok (implements)
Instance alanOlabilirOlamaz (sadece sabit)
ConstructorOlabilirOlamaz
Gövdeli metotNormal metotlarSadece default, static, private
Metot erişimiHer türlüVarsayılan public
İlişkiis-a (ortak kod paylaşımı)can-do (yetenek)
ÖrnekEmployee, ShapePayable, Comparable, Runnable

Pratik kural: alt sınıflar ortak alan ve kod paylaşıyorsa abstract sınıf. Sadece ortak yetenek varsa arayüz.

Comparable ve compareTo

java.lang.Comparable<T> tek metotlu bir arayüz: int compareTo(T other). Bir sınıf bunu uygulayınca nesneleri için “doğal sıra” tanımlamış olur. Arrays.sort ve Collections.sort bu sırayı kullanır.

compareTo’nun döndürdüğü değerin anlamı:

a.compareTo(b)Anlamı
negatifa, b’den önce gelir
0eşit sayılırlar
pozitifa, b’den sonra gelir
class Student implements Comparable<Student> {
    String name; double gpa;
    Student(String n, double g) { name = n; gpa = g; }

    @Override
    public int compareTo(Student o) { return Double.compare(gpa, o.gpa); }

    @Override
    public String toString() { return name + "(" + gpa + ")"; }
}

Student[] st = { new Student("Ece", 3.1), new Student("Ali", 3.8), new Student("Can", 2.5) };
Arrays.sort(st);
System.out.println(Arrays.toString(st));
System.out.println(st[0].compareTo(st[2]));
System.out.println("apple".compareTo("banana"));
System.out.println("b".compareTo("a"));
System.out.println(Integer.valueOf(5).compareTo(5));
[Can(2.5), Ece(3.1), Ali(3.8)]
-1
-1
1
0

İPUCU · ters sıra. Büyükten küçüğe sıralamak için argümanları değiştir: Double.compare(o.gpa, gpa). İsme göre sıralamak için String’in kendi compareTo’sunu kullan: name.compareTo(o.name).

String, Integer, Double gibi hazır sınıflar zaten Comparable. Bu yüzden String[] dizisini doğrudan Arrays.sort ile alfabetik dizebilirsin. Kendi sınıfın Comparable değilse ve Arrays.sort çağırırsan derleme hatası almazsın, ama çalışırken ClassCastException fırlar.

Arayüz tipi ve instanceof

Arayüz de bir tiptir: Payable p = new Invoice(...) geçerli, p instanceof Payable çalışır. Arayüz tipindeki değişkenle sadece arayüzde tanımlı metotları çağırabilirsin (geçen haftaki static tip kuralı aynen geçerli).

Object o = new Invoice(1, 1);
System.out.println(o instanceof Payable);
System.out.println(o instanceof Comparable);
true
false

Sık yapılan hatalar

Yanlış düşünceDoğrusu
Abstract sınıfın constructor’ı olmazOlur, alt sınıf super(...) ile çağırır
new Shape() abstract’ta da çalışırDerleme hatası; sadece somut alt sınıflardan new
Abstract metoda { } yazılabilirGövde yok, ; ile biter
Abstract metodu olan normal sınıfSınıf da abstract olmak zorunda
Interface metodunu sınıfta public’siz yazmakErişim daralır, derleme hatası
Interface alanı değişken gibi değişirpublic static final, değiştirilemez
class A implements X extends BSıra: extends B implements X
İki arayüzden aynı default gelirse ilki seçilirDerleme hatası, sınıf override etmeli
compareTo true/false dönerint döner: negatif, 0, pozitif
return cents - other.cents; her zaman doğruÇok büyük değerlerde taşar; Integer.compare

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

Find the compile error.

abstract class Shape { abstract double area(); }
// in main:
Shape s = new Shape();
Cevabı göster
Abstract sınıftan nesne yaratılamaz

javac: Shape is abstract; cannot be instantiated. Abstract sınıf tip olarak kullanılabilir (Shape s = new Circle(2); geçerli) ama kendisi new ile yaratılamaz.

Soru 2kolay

Find the compile error.

abstract class Shape { abstract double area(); }
class Circle extends Shape {
    double r;
}
Cevabı göster
Circle abstract değil ama area() metodunu yazmamış

Somut (concrete) bir alt sınıf, miras aldığı her abstract metodu gövdesiyle yazmak zorunda. javac: Circle is not abstract and does not override abstract method area() in Shape. Çözüm: double area() { return Math.PI * r * r; } ekle ya da Circle'ı da abstract yap.

Soru 3orta

Find the compile error.

interface Payable { double amount(); }
class Bill implements Payable {
    double amount() { return 10; }
}
Cevabı göster
amount() public yazılmalı

Arayüz metotları otomatik olarak public abstract. Sınıfta belirleyici yazılmazsa package-private olur, bu da erişimi daraltır. javac: attempting to assign weaker access privileges; was public. Çözüm: public double amount().

Soru 4kolay

Find the compile error.

class Shape {
    abstract double area();
}
Cevabı göster
Abstract metot içeren sınıf abstract olmalı

Bir sınıfta tek bir abstract metot bile varsa sınıf abstract işaretlenmek zorunda. javac burada Shape is not abstract and does not override abstract method area() in Shape der. Çözüm: abstract class Shape.

Soru 5orta

What is printed?

abstract class Employee {
    private final String name;
    Employee(String name) { this.name = name; }
    String getName() { return name; }
    abstract double earnings();
    public String toString() { return getName() + ": " + earnings(); }
}
class Hourly extends Employee {
    private double rate; private int hours;
    Hourly(String n, double r, int h) { super(n); rate = r; hours = h; }
    double earnings() {
        if (hours <= 40) return rate * hours;
        return rate * 40 + rate * 1.5 * (hours - 40);
    }
}
// in main:
System.out.println(new Hourly("Bora", 100, 45));
Cevabı göster
Bora: 4750.0

toString abstract sınıfta, earnings çağrısı Hourly'nin sürümüne gider. 40 saat normal: 100 · 40 = 4000. 5 saat fazla mesai: 100 · 1.5 · 5 = 750. Toplam 4750.0. Abstract sınıfın constructor'ı vardır ve super(n) ile çalışır.

Soru 6orta

What is printed?

interface Payable {
    double TAX = 0.2;
    double amount();
    default double afterTax() { return amount() * (1 - TAX); }
}
class Invoice implements Payable {
    private int qty; private double price;
    Invoice(int q, double p) { qty = q; price = p; }
    public double amount() { return qty * price; }
}
// in main:
Payable p = new Invoice(3, 50);
System.out.println(p.amount() + " " + p.afterTax());
Cevabı göster
150.0 120.0

amount: 3 · 50.0 = 150.0. afterTax default metot, Invoice yazmadığı için arayüzdeki sürüm çalışır; içindeki amount() yine Invoice'unki: 150 · 0.8 = 120.0. TAX arayüzde tanımlı olduğu için otomatik public static final.

Soru 7zor

Find the compile error.

interface Flyer   { default String move() { return "fly"; } }
interface Swimmer { default String move() { return "swim"; } }
class Duck implements Flyer, Swimmer { }
Cevabı göster
İki arayüz aynı default metodu getiriyor, Duck move() metodunu override etmeli

javac: types Flyer and Swimmer are incompatible; class Duck inherits unrelated defaults for move(). Hangisinin geçerli olduğuna sınıf karar vermeli: public String move() { return Flyer.super.move() + "+" + Swimmer.super.move(); } yazınca fly+swim döner.

Soru 8orta

What is printed?

class Student implements Comparable<Student> {
    String name; double gpa;
    Student(String n, double g) { name = n; gpa = g; }
    public int compareTo(Student o) { return Double.compare(gpa, o.gpa); }
    public String toString() { return name + "(" + gpa + ")"; }
}
// in main:
Student[] st = { new Student("Ece", 3.1), new Student("Ali", 3.8), new Student("Can", 2.5) };
Arrays.sort(st);
System.out.println(Arrays.toString(st));
Cevabı göster
[Can(2.5), Ece(3.1), Ali(3.8)]

Arrays.sort, compareTo'yu kullanarak küçükten büyüğe dizer. compareTo gpa'yı karşılaştırıyor, yani sıra not ortalamasına göre artan: 2.5, 3.1, 3.8. Büyükten küçüğe isteseydin Double.compare(o.gpa, gpa) yazardın.

Soru 9orta

What is printed?

System.out.println("apple".compareTo("banana"));
System.out.println("b".compareTo("a"));
System.out.println(Integer.valueOf(5).compareTo(5));
Cevabı göster
-1, 1, 0

compareTo sözleşmesi: negatif = önce gelir, 0 = eşit, pozitif = sonra gelir. String'de ilk farklı karakterlerin kod farkı döner: 'a' − 'b' = −1, 'b' − 'a' = 1. Integer eşitlikte 0 verir. Sınavda çoğu zaman sadece işaret sorulur.

Soru 10orta

What is printed by new Impl();?

abstract class Base {
    Base() { System.out.println("Base " + id()); }
    abstract String id();
}
class Impl extends Base {
    Impl() { super(); System.out.println("Impl"); }
    String id() { return "impl-id"; }
}
Cevabı göster
Base impl-id / Impl

Abstract sınıfın constructor'ı alt sınıf nesnesi kurulurken çalışır. İçindeki abstract id() çağrısı, nesne Impl olduğu için Impl'in sürümüne gider: Base impl-id. Sonra Impl'in gövdesi: Impl.

Soru 11kolay

Which is TRUE? (a) A class can extend two abstract classes. (b) An interface can have instance fields. (c) A class can implement several interfaces. (d) An abstract class cannot have a constructor.

  1. (a)
  2. (b)
  3. (c)
  4. (d)
Cevabı göster
(c)

Java'da tek extends, ama istediğin kadar implements. Arayüzdeki alanlar her zaman public static final sabittir, instance alan olamaz; (b) yanlış. Abstract sınıfın constructor'ı olabilir ve alt sınıf super(...) ile çağırır; (d) yanlış.

Soru 12orta

Write a class Money that stores an amount in cents (int) and implements Comparable<Money> so that Arrays.sort orders Money objects from smallest to largest. toString() returns e.g. 120c.

Cevabı göster
implements Comparable<Money> + Integer.compare

compareTo negatif / 0 / pozitif döndürmeli. Çıkarma (cents - other.cents) çok büyük sayılarda taşabilir; Integer.compare güvenlidir:

public class Money implements Comparable<Money> {
    private final int cents;

    public Money(int cents) { this.cents = cents; }

    @Override
    public int compareTo(Money other) {
        return Integer.compare(cents, other.cents);
    }

    @Override
    public String toString() { return cents + "c"; }
}
{500c, 120c, 300c} dizisi Arrays.sort sonrası [120c, 300c, 500c] olur.

Doğrulama: Tüm kod örnekleri ve cevaplar JDK 26 ile çalıştırıldı (java verify/SEN1002/W04.java, W04b.java); derleme hataları javac ile denendi (verify/SEN1002/err/E04a-g.java).

Deftere not kartı

abstract class: new YAPILAMAZ, tip olarak kullanılır. Alan, constructor, somut metot olabilir. abstract metot: gövde yok, noktalı virgülle biter: abstract double area(); Bir abstract metot varsa sınıf da abstract olmak ZORUNDA. Somut alt sınıf bütün abstract metotları yazmalı, yoksa "is not abstract and does not override". interface: metotlar otomatik public abstract; alanlar public static final (sabit). implements eden sınıfta metotlar PUBLIC yazılır (yoksa weaker access hatası). default metot: gövdeli, override edilebilir. static metot: Arayuz.metot() ile çağrılır. extends tek, implements çok: class D extends B implements X, Y İki arayüz aynı default'u getirirse sınıf override etmeli: X.super.m() Comparable<T>: int compareTo(T o) → negatif önce, 0 eşit, pozitif sonra. Arrays.sort(dizi) compareTo ile artan sıralar. Ters sıra: argümanları yer değiştir. Integer.compare(a,b), Double.compare(a,b) kullan; a-b taşabilir.