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ğı.
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:
- Abstract sınıftan
newyapılmaz ama tip olarak kullanılır. - Abstract metodu olan sınıf abstract olmak zorunda. Somut alt sınıf hepsini yazmak zorunda.
- Arayüz metotları public’tir. Implement ederken
publicyazmayı unutma. - Tek
extends, çokimplements. - 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
| Durum | Sonuç |
|---|---|
new AbstractSinif() | Derleme hatası |
| Sınıfta abstract metot var ama sınıf abstract değil | Derleme 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 abstract | Serbest, metotları yazmak zorunda değil; sorumluluk bir sonraki somut sınıfa geçer |
| Abstract sınıfta constructor | Var olabilir, super(...) ile çağrılır |
abstract + private / static / final metot | Derleme 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ığın | Derleyicinin 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 ·
publicunutmak. Arayüzdepublicyazmasan 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ıf | Interface | |
|---|---|---|
| Kaç tanesinden türeyebilir? | Bir (extends) | Çok (implements) |
| Instance alan | Olabilir | Olamaz (sadece sabit) |
| Constructor | Olabilir | Olamaz |
| Gövdeli metot | Normal metotlar | Sadece default, static, private |
| Metot erişimi | Her türlü | Varsayılan public |
| İlişki | is-a (ortak kod paylaşımı) | can-do (yetenek) |
| Örnek | Employee, Shape | Payable, 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ı |
|---|---|
| negatif | a, b’den önce gelir |
| 0 | eşit sayılırlar |
| pozitif | a, 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üşünce | Doğrusu |
|---|---|
| Abstract sınıfın constructor’ı olmaz | Olur, alt sınıf super(...) ile çağırır |
new Shape() abstract’ta da çalışır | Derleme hatası; sadece somut alt sınıflardan new |
Abstract metoda { } yazılabilir | Gövde yok, ; ile biter |
| Abstract metodu olan normal sınıf | Sınıf da abstract olmak zorunda |
| Interface metodunu sınıfta public’siz yazmak | Erişim daralır, derleme hatası |
| Interface alanı değişken gibi değişir | public static final, değiştirilemez |
class A implements X extends B | Sıra: extends B implements X |
| İki arayüzden aynı default gelirse ilki seçilir | Derleme hatası, sınıf override etmeli |
| compareTo true/false döner | int 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ç.
Find the compile error.
abstract class Shape { abstract double area(); }
// in main:
Shape s = new Shape();Cevabı göster
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.
Find the compile error.
abstract class Shape { abstract double area(); }
class Circle extends Shape {
double r;
}Cevabı göster
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.
Find the compile error.
interface Payable { double amount(); }
class Bill implements Payable {
double amount() { return 10; }
}Cevabı göster
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().
Find the compile error.
class Shape {
abstract double area();
}Cevabı göster
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.
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
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.
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
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.
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
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.
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
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.
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
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.
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
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.
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.
- (a)
- (b)
- (c)
- (d)
Cevabı göster
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ış.
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
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).