Composition, enum ve referansların metoda geçişi
Bir sınıfın alanı olarak başka bir sınıfın nesnesi (has-a), sabit değer kümeleri için enum, ve nesne referanslarının metoda kopya olarak gitmesinin sonuçları: metot nesneyi değiştirebilir ama çağıranın değişkenini başka nesneye çeviremez.
Bu hafta üç konu var ve üçü de nesnelerin birbirleriyle ilişkisi hakkında. Composition, bir nesnenin içinde başka nesneler olmasıdır (çalışanın doğum tarihi, arabanın motoru). enum, sabit ve sınırlı bir değer kümesini (günler, seviyeler, gezegenler) güvenli şekilde temsil eder. Son olarak, nesneleri metoda verince neyin değişip neyin değişmediğini kesin olarak öğreneceksin. Bu üçüncü konu, final sınavındaki “what is printed?” sorularının en sinsi kaynağıdır.
Altın kurallar:
- Composition = has-a. Bir sınıfın alanı başka bir sınıfın nesnesi olabilir.
- enum sabitleri new ile üretilmez,
Day.WEDdiye kullanılır ve==ile karşılaştırılır. - Java her zaman pass-by-value. Nesnelerde kopyalanan şey referanstır.
- Metot nesnenin içini değiştirebilir; çağıranın değişkenini başka nesneye çeviremez.
==aynı nesne mi diye bakar,equalsiçeriğe (override edildiyse).
Composition: has-a ilişkisi
class Date {
private int day, month, year;
public Date(int day, int month, int year) {
this.day = day; this.month = month; this.year = year;
}
public int getYear() { return year; }
public String toString() { return day + "/" + month + "/" + year; }
...
}
class Employee {
private String name;
private Date birthDate; // başka bir sınıfın nesnesi
public Employee(String name, Date birthDate) {
this.name = name;
this.birthDate = birthDate;
}
public Date getBirthDate() { return birthDate; }
public String toString() { return name + ", born " + birthDate; }
}
// main içinde:
Date birth = new Date(14, 3, 2005);
Employee e = new Employee("Deniz", birth);
System.out.println(e);
System.out.println(e.getBirthDate().getYear());
Deniz, born 14/3/2005
2005
- Employee’nin toString’i
birthDate’i bir String ile birleştirince Date’in toString’i otomatik çağrıldı. e.getBirthDate().getYear()bir zincir: önce Date nesnesini al, sonra onun metodunu çağır.- “Bir çalışan bir tarih sahibidir” cümlesi doğru geliyorsa composition doğru seçimdir.
İkinci örnek, işi iç nesneye devretmek (delegation):
class Engine {
private int cc;
public Engine(int cc) { this.cc = cc; }
public int getCc() { return cc; }
public void setCc(int cc) { this.cc = cc; }
public void run() { System.out.println("Engine " + cc + "cc running"); }
}
// Car: private String model; private Engine engine; start() { print(model + ": "); engine.run(); }
Car car = new Car("Egea", new Engine(1400));
car.start();
System.out.println(car.getEngine().getCc());
Egea: Engine 1400cc running
1400
TUZAK · getter iç nesneyi dışarı verir.
getBirthDate()Date’in kopyasını değil, referansını döndürür. Ayrıca kurucuya verilen Date nesnesi de kopyalanmadı: aynı referans saklandı. Dışarıdadd.setYear(1990)yapınca çalışanın doğum yılı da 1990 oldu (doğrulamadae2.getBirthDate().getYear()→ 1990). Alan private olsa bile, değiştirilebilir bir iç nesneyi paylaşırsan içini koruyamazsın.
enum
Bazı değişkenler sadece birkaç değerden birini alabilir: haftanın günü, trafik ışığı, zorluk seviyesi. Bunları int ile (0 = Pazartesi) ya da String ile ("Mon") tutmak hataya açıktır: day = 9 ya da "mon" yazmak kimseyi durdurmaz. enum bu kümeyi bir tip olarak tanımlar:
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
Day d = Day.WED;
System.out.println(d + " " + d.ordinal() + " " + d.name());
System.out.println(d == Day.WED);
for (Day x : Day.values()) System.out.print(x + " ");
System.out.println();
System.out.println(Day.values().length);
System.out.println(Day.valueOf("FRI").ordinal());
WED 2 WED
true
MON TUE WED THU FRI SAT SUN
7
4
| İfade | Ne verir |
|---|---|
Day.WED | sabitin kendisi |
d.ordinal() | tanım sırası, 0’dan |
d.name(), println(d) | adı: "WED" |
Day.values() | bütün sabitler, sırayla, dizi olarak |
Day.valueOf("FRI") | String’den sabit; yoksa IllegalArgumentException |
d == Day.WED | karşılaştırma (enum’da == güvenli) |
new Day() yazılamaz: enum classes may not be instantiated. Sabitler programın başında bir kez oluşturulur, o kadar.
enum ile switch
static String advice(Level lv) {
switch (lv) {
case LOW: return "Devam"; // Level.LOW değil, sadece LOW
case MEDIUM: return "Dikkat";
case HIGH: return "Dur";
default: return "?";
}
}
// main: advice(Level.LOW) + " " + advice(Level.HIGH)
Devam Dur
Alanlı ve kuruculu enum
enum aslında özel bir sınıftır; alanı, kurucusu ve metodu olabilir. Her sabit kurucuyu kendi argümanıyla çağırır:
enum Coin {
QUARTER(25), HALF(50), LIRA(100); // sabitlerden sonra ;
private final int kurus;
Coin(int k) { kurus = k; } // kurucu public yazılmaz
public int getKurus() { return kurus; }
}
// main içinde:
for (Coin c : Coin.values()) System.out.print(c + "=" + c.getKurus() + " ");
System.out.println();
int total = Coin.HALF.getKurus() * 2 + Coin.QUARTER.getKurus();
System.out.println(total);
QUARTER=25 HALF=50 LIRA=100
125
Referansların metoda geçişi
Java’da her şey pass-by-value: metoda bir değerin kopyası gider. Primitive için kopyalanan şey sayının kendisidir. Nesne için kopyalanan şey referanstır (nesnenin adresi). Bu tek kuralın üç sonucu var:
static void rename(Pet p) { p.setName("Boncuk"); } // 1) içini değiştir
static void replace(Pet p) { p = new Pet("Yeni"); p.setName("Degisti"); } // 2) yeni nesneye çevir
static void swap(Pet a, Pet b) { Pet t = a; a = b; b = t; } // 3) takas
// main içinde:
Pet pet = new Pet("Pamuk");
rename(pet);
System.out.println(pet.getName());
replace(pet);
System.out.println(pet.getName());
Pet a = new Pet("A"), b = new Pet("B");
swap(a, b);
System.out.println(a.getName() + " " + b.getName());
Boncuk
Boncuk
A B
rename çağrısı:
main.pet ──┐
├──► [Pet: "Pamuk" → "Boncuk"] aynı nesne, içi değişti
rename.p ──┘
replace çağrısı:
main.pet ─────► [Pet: "Boncuk"] dokunulmadı
replace.p ────► [Pet: "Degisti"] yeni nesne, metot bitince kaybolur
| Metot ne yapıyor? | Çağıran görür mü? |
|---|---|
| Parametre üzerinden nesnenin metodunu çağırıp içini değiştiriyor | Evet |
Parametreye yeni bir nesne atıyor (p = new ...) | Hayır |
| İki parametreyi takas ediyor | Hayır |
| Primitive parametreyi değiştiriyor | Hayır |
| Yeni nesne oluşturup return ediyor | Evet, dönen değeri atarsan |
Metottan yeni bir nesne almak istiyorsan onu döndür: Pet m = make("Minnos");.
Sınavda bu tip bir soru görünce her metot çağrısı için kendine şunu sor: metot parametre üzerinden bir setter ya da eleman ataması mı yapıyor (çağıran görür), yoksa parametrenin kendisine mi atama yapıyor (çağıran görmez)? Cevabı bu tek soru belirler.
== ve equals
Pet x1 = new Pet("Tom"), x2 = new Pet("Tom");
System.out.println((x1 == x2) + " " + x1.equals(x2));
Point p1 = new Point(1, 2), p2 = new Point(1, 2);
System.out.println((p1 == p2) + " " + p1.equals(p2));
false false
false true
Pet equals’ı override etmedi; Object’ten gelen varsayılan equals == ile aynı şeyi yapar. Point ise equals’ı kendisi tanımladı ve x, y’yi karşılaştırıyor:
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point other = (Point) o;
return x == other.x && y == other.y;
}
String’in equals’ı da böyle override edilmiş bir metot; "abc".equals(s) bu yüzden içeriğe bakar.
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
| Composition için alanı public yap | private alan + kurucu + gerekirse getter |
| Getter iç nesnenin kopyasını verir | Referansını verir |
Day d = new Day(); | Day d = Day.MON; |
case Day.MON: | case MON: |
ordinal() 1’den başlar | 0’dan |
valueOf("mon") çalışır | Büyük/küçük harf duyarlı; yoksa exception |
| Java nesneleri pass-by-reference geçirir | Pass-by-value; kopyalanan şey referans |
Metot p = new Pet(...) deyince main’deki nesne değişir | Değişmez |
| swap metodu nesneleri takas eder | Etmez |
x1 == x2 içerik karşılaştırır | Aynı nesne mi; içerik için equals |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
Employee has a Date birthDate field; Date(day, month, year) has getters. What is printed?
Employee q = new Employee("Ece", new Date(5, 9, 2004));
System.out.println(q.getBirthDate().getMonth() + q.getBirthDate().getDay());Cevabı göster
getBirthDate() bir Date nesnesi döndürür, üzerinden hemen başka bir metot çağrılabilir (zincir). Ay 9, gün 5; ikisi de int, toplama: 14.
Given enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }, what is printed?
System.out.println(Day.MON.ordinal() + Day.SUN.ordinal() + " " + Day.values()[2]);Cevabı göster
ordinal() sabitin sırası, 0'dan başlar: MON 0, SUN 6. 0 + 6 = 6 (int toplama). values() sabitleri tanım sırasıyla bir dizi olarak verir; 2. indeks WED. enum println'da adıyla basılır.
Given enum Level { LOW, MEDIUM, HIGH }, what is printed?
Level lv = Level.MEDIUM;
switch (lv) {
case LOW: System.out.print("L");
case MEDIUM: System.out.print("M");
case HIGH: System.out.print("H");
}Cevabı göster
enum ile switch yazılabilir; case'lerde sınıf adı yazılmaz (case LOW, Level.LOW değil). MEDIUM'dan girilir, break olmadığı için HIGH'a düşer: MH.
rename(Pet p) calls p.setName("Boncuk"). What is printed?
Pet q = new Pet("Zeytin");
Pet q2 = q;
rename(q2);
System.out.println(q.getName());Cevabı göster
q ve q2 aynı nesneyi gösterir. rename'e o nesnenin referansının kopyası gider; metot aynı nesnenin adını değiştirir. Üç referans da tek bir nesneye bakıyor.
replace(Pet p) does p = new Pet("Yeni"); p.setName("Degisti"); and rename sets the name to Boncuk. What is printed?
Pet q = new Pet("Fistik");
replace(q);
rename(q);
replace(q);
System.out.println(q.getName());Cevabı göster
replace içinde p = new Pet(...) sadece metodun yerel kopyasını yeni nesneye çevirir; main'deki q hâlâ eski nesnede. replace'in değiştirdiği nesne metot bitince kaybolur. Sadece rename'in etkisi kalır.
What is printed?
static void modify(int[] arr, int v) { arr[0] = 100; v = 100; }
// in main:
int[] nums = {1, 2, 3};
int k = 5;
modify(nums, k);
System.out.println(nums[0] + " " + k);Cevabı göster
İkisi de pass-by-value. Dizi için kopyalanan şey referans; metot aynı dizinin elemanını değiştirir. int için kopyalanan şey değerin kendisi; v'yi değiştirmek k'ya dokunmaz.
Car has a private Engine engine field and getEngine() returns it; Engine has getCc() and setCc(int). What is printed?
Car c = new Car("Clio", new Engine(1000));
Engine en = c.getEngine();
en.setCc(1600);
System.out.println(c.getEngine().getCc());Cevabı göster
Getter nesnenin kopyasını değil, referansını döndürür. en, arabanın içindeki motorun ta kendisi; onu değiştirmek arabanın motorunu değiştirir. Alan private olsa bile değiştirilebilir bir nesne dışarı verilirse içi korunmaz.
enum Coin { QUARTER(25), HALF(50), LIRA(100); ... } has getKurus(). What is printed?
System.out.println(Coin.valueOf("QUARTER").getKurus() + Coin.values().length);Cevabı göster
valueOf("QUARTER") String'den sabiti bulur; kuruş değeri 25. values().length sabit sayısı: 3. 25 + 3 = 28.
Point overrides equals to compare x and y. What is printed?
Point a = new Point(3, 4), b = new Point(3, 4), c = a;
System.out.println((a == b) + " " + a.equals(b) + " " + (a == c));Cevabı göster
== aynı nesne mi diye bakar: a ve b iki ayrı nesne → false. equals override edildiği için içeriği karşılaştırır → true. c = a aynı nesne → true.
Given enum Level { LOW, HIGH }: (a) does Level x = new Level(); compile? (b) what happens at runtime with Level.valueOf("MID")?
Cevabı göster
enum sabitleri sabittir; new ile yenisi üretilemez, derleyici enum classes may not be instantiated der. valueOf olmayan bir isimle çağrılırsa derlenir ama çalışırken No enum constant Level.MID ile çöker.
Write an enum Planet with constants EARTH(9.81) and MARS(3.71), a private final double field gravity, a constructor and a getter.
Cevabı göster
Sabitlerden sonra ; konur, alanlar ve metotlar ondan sonra gelir. enum kurucusu public yazılmaz (otomatik private).enum Planet { EARTH(9.81), MARS(3.71); private final double gravity; Planet(double g) { gravity = g; } public double getGravity() { return gravity; }}Planet.MARS + " " + Planet.MARS.getGravity() → MARS 3.71.
Write a class Car that has a model (String) and an Engine (composition), a constructor taking both, and a start() method that prints the model followed by the result of the engine's run().
Cevabı göster
Car bir Engine'e sahip (has-a). Car işi kendisi yapmaz, motora devreder (delegation).class Car { private String model; private Engine engine; public Car(String model, Engine engine) { this.model = model; this.engine = engine; } public void start() { System.out.print(model + ": "); engine.run(); }}new Car("Egea", new Engine(1400)).start() → Egea: Engine 1400cc running.
Doğrulama: Tüm çıktılar JDK 26 ile çalıştırıldı (verify/SEN1001/W13.java; Date, Employee, Engine, Car, Pet, Point sınıfları ve Day, Level, Planet, Coin enum'ları aynı dosyada). enum hataları verify/SEN1001/err/en/ altında javac ve java ile alındı.