Tür Dönüşümü (Type Casting)
Java katı tipli (strongly typed) bir dil. Bir int değişkene String atayamazsın, bir double değeri int'e direkt koyman tehlikeli olabilir. Ama bazen bir tipi diğerine çevirmen gerekir — işte buna tür dönüşümü (type casting) diyoruz.
Bunu şöyle düşün: Farklı boyutta kaplar var. Küçük bardaktaki suyu büyük kovaya kolayca boşaltabilirsin (widening). Ama büyük kovadaki suyu küçük bardağa dökerken taşar (narrowing) — dikkatli olmalısın.
İki Tür Dönüşüm
| Tür | Yön | Güvenlik | Cast Gerekli mi? |
|---|---|---|---|
| Widening (Genişletme) | Küçük → Büyük | Güvenli | Hayır (otomatik) |
| Narrowing (Daraltma) | Büyük → Küçük | Riskli | Evet (explicit) |
Tip Hiyerarşisi (Küçükten Büyüğe)
byte → short → int → long → float → double
↑
charSoldan sağa doğru otomatik dönüşüm olur. Sağdan sola explicit cast gerekir.
Widening (Genişletme) — Otomatik Dönüşüm
Küçük tipten büyük tipe dönüşüm güvenlidir ve Java otomatik yapar. Veri kaybı olmaz.
byte b = 42;
short s = b; // byte → short (otomatik)
int i = s; // short → int (otomatik)
long l = i; // int → long (otomatik)
float f = l; // long → float (otomatik)
double d = f; // float → double (otomatik)
System.out.println(d); // 42.0// Pratikte sık karşılaşılan durumlar
int sayi = 100;
double sonuc = sayi; // int → double (otomatik)
System.out.println(sonuc); // 100.0
char c = 'A';
int ascii = c; // char → int (otomatik)
System.out.println(ascii); // 65Aritmetikte Otomatik Widening
Java, aritmetik işlemlerde operandları otomatik olarak ortak tipe yükseltir:
int a = 10;
double b = 3.0;
double sonuc = a / b; // a otomatik double'a çevrilir
System.out.println(sonuc); // 3.3333...
byte x = 10;
byte y = 20;
// byte z = x + y; // HATA! x + y sonucu int'tir
int z = x + y; // Doğru⚠️ Önemli kural: byte, short ve char aritmetik işlemlerde otomatik olarak int'e yükseltilir. Bu yüzden iki byte toplamını byte'a atayamazsın — int veya explicit cast gerekir.
byte a = 10;
byte b = 20;
// byte c = a + b; // HATA! int → byte otomatik olmaz
byte c = (byte)(a + b); // OK, explicit cast
int d = a + b; // OK, int'te kalWidening'de Dikkat: long → float
long buyukSayi = 123456789012345L;
float f = buyukSayi; // Otomatik ama hassasiyet kaybı!
System.out.println(f); // 1.23456792E14
System.out.println(buyukSayi); // 123456789012345
// float sadece ~7 basamak hassasiyet sunar
// long 15+ basamak olabilir → veri kaybı!💡 Widening "güvenli" deniyor ama
long → floatdönüşümünde hassasiyet kaybı olabilir.long → doubledaha güvenli ama o da uç değerlerde sorun çıkarabilir.
Narrowing (Daraltma) — Explicit Cast
Büyük tipten küçük tipe dönüşüm risklidir — veri kaybı olabilir. Java otomatik yapmaz, senin açıkça belirtmen gerekir.
Sözdizimi: (hedefTip) deger
double pi = 3.14159;
int tamsayi = (int) pi; // Ondalık kısım ATILIR (yuvarlanmaz!)
System.out.println(tamsayi); // 3
long buyuk = 130;
byte kucuk = (byte) buyuk; // 130 byte aralığını aşıyor!
System.out.println(kucuk); // -126 (taşma!)Veri Kaybı Örnekleri
// double → int: ondalık kısım atılır
System.out.println((int) 3.99); // 3 (yuvarlanmaz!)
System.out.println((int) -2.7); // -2
// int → byte: taşma olur
System.out.println((byte) 128); // -128
System.out.println((byte) 256); // 0
System.out.println((byte) 300); // 44
// int → short
System.out.println((short) 40000); // -25536Taşma nasıl çalışır? Narrowing cast'ta Java sadece alt bitleri alır, üst bitleri atar:
int sayi = 300; // 00000000 00000000 00000001 00101100
byte b = (byte) sayi; // Son 8 bit: 00101100 = 44
System.out.println(b); // 44Güvenli Narrowing — Kontrol Et
long deger = 42;
if (deger >= Byte.MIN_VALUE && deger <= Byte.MAX_VALUE) {
byte b = (byte) deger;
System.out.println("Güvenli: " + b);
} else {
System.out.println("Byte aralığının dışında!");
}// Java 8+ — Math.toIntExact()
long buyuk = 3000000000L;
try {
int kucuk = Math.toIntExact(buyuk); // ArithmeticException!
} catch (ArithmeticException e) {
System.out.println("Long int'e sığmıyor: " + buyuk);
}char Dönüşümleri
char 16-bit unsigned (işaretsiz) tiptir. Sayısal tiplere çevrilebilir ve sayısal tiplerden çevrilebilir.
char c = 'A';
int i = c; // char → int (widening, otomatik): 65
double d = c; // char → double (widening, otomatik): 65.0
int sayi = 66;
char harf = (char) sayi; // int → char (narrowing, explicit)
System.out.println(harf); // 'B'
// Karakter aritmetiği
char buyukA = 'A';
char kucukA = (char)(buyukA + 32); // 65 + 32 = 97 = 'a'
System.out.println(kucukA); // 'a'⚠️ byte ve short'tan char'a dönüşüm de narrowing sayılır (çünkü char unsigned, diğerleri signed):
short s = 65;
// char c = s; // HATA! short → char narrowing
char c = (char) s; // OKWrapper Sınıflar
Her primitive tipin bir nesne karşılığı (wrapper class) vardır:
| Primitive | Wrapper | Örnek |
|---|---|---|
| byte | Byte | Byte.valueOf((byte)5) |
| short | Short | Short.valueOf((short)100) |
| int | Integer | Integer.valueOf(42) |
| long | Long | Long.valueOf(100L) |
| float | Float | Float.valueOf(3.14f) |
| double | Double | Double.valueOf(2.718) |
| char | Character | Character.valueOf('A') |
| boolean | Boolean | Boolean.valueOf(true) |
Neden Wrapper'lar Gerekli?
Koleksiyonlar primitive alamaz:
List<int>yazamazsın,List<Integer>yazarsınnull değer: Primitive'ler null olamaz, wrapper'lar olabilir
Utility metotlar:
Integer.parseInt(),Double.parseDouble()vb.
// Koleksiyonlarda wrapper zorunlu
List<Integer> sayilar = new ArrayList<>();
sayilar.add(42);
sayilar.add(17);
// null olabilir
Integer nullable = null; // OK
// int primitive = null; // HATA!Utility Metotlar
// String → Sayı dönüşümü
int sayi = Integer.parseInt("42");
double ondalik = Double.parseDouble("3.14");
long buyuk = Long.parseLong("1000000000");
boolean bool = Boolean.parseBoolean("true");
System.out.println(sayi); // 42
System.out.println(ondalik); // 3.14
// Sayı → String dönüşümü
String s1 = Integer.toString(42);
String s2 = String.valueOf(42);
String s3 = "" + 42; // Pratik ama ideal değil
// Farklı tabanda gösterim
System.out.println(Integer.toBinaryString(42)); // "101010"
System.out.println(Integer.toHexString(255)); // "ff"
System.out.println(Integer.toOctalString(8)); // "10"
// Farklı tabandan parse etme
int fromHex = Integer.parseInt("ff", 16); // 255
int fromBin = Integer.parseInt("101010", 2); // 42
int fromOct = Integer.parseInt("52", 8); // 42Wrapper Karşılaştırma Metotları
// compare() — iki primitive değer karşılaştır
System.out.println(Integer.compare(10, 20)); // -1 (küçük)
System.out.println(Integer.compare(20, 20)); // 0 (eşit)
System.out.println(Integer.compare(30, 20)); // 1 (büyük)
// max() ve min()
System.out.println(Integer.max(10, 20)); // 20
System.out.println(Integer.min(10, 20)); // 10
System.out.println(Double.max(3.14, 2.72)); // 3.14
// sum()
System.out.println(Integer.sum(10, 20)); // 30
System.out.println(Long.sum(100L, 200L)); // 300Özel Değer Kontrolleri
// Double özel değerleri
System.out.println(Double.isNaN(0.0 / 0.0)); // true
System.out.println(Double.isInfinite(1.0 / 0.0)); // true
System.out.println(Double.isFinite(42.0)); // true
// Integer sınırları
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MIN_VALUE); // -2147483648
System.out.println(Integer.BYTES); // 4
System.out.println(Integer.SIZE); // 32 (bit)parseInt Hata Durumu
try {
int sayi = Integer.parseInt("abc"); // NumberFormatException!
} catch (NumberFormatException e) {
System.out.println("Geçersiz sayı formatı");
}
// Boşlukları temizle
String girdi = " 42 ";
int sayi = Integer.parseInt(girdi.trim()); // 42 — OKAutoboxing ve Unboxing
Java 5+ ile birlikte primitive ↔ wrapper dönüşümleri otomatik yapılır.
Autoboxing: Primitive → Wrapper (otomatik) Unboxing: Wrapper → Primitive (otomatik)
// Autoboxing: int → Integer
Integer sayi = 42; // Java otomatik Integer.valueOf(42) çağırır
// Unboxing: Integer → int
int primitive = sayi; // Java otomatik sayi.intValue() çağırır
// İşlemlerde otomatik
Integer a = 10;
Integer b = 20;
int toplam = a + b; // Unbox → topla → sonuç int// List ile kullanım
List<Integer> sayilar = new ArrayList<>();
sayilar.add(42); // Autoboxing: int → Integer
int ilk = sayilar.get(0); // Unboxing: Integer → intAutoboxing Tuzakları
1. null Unboxing — NullPointerException:
Integer sayi = null;
int x = sayi; // NullPointerException! null unbox edilemezBu çok sinsi bir hata. Wrapper null olabilir ama primitive olamaz. Unboxing sırasında null ise patlarsın.
// Güvenli yol
Integer sayi = getSayiFromDb(); // null dönebilir
int x = (sayi != null) ? sayi : 0; // Güvenli2. == ile Karşılaştırma Tuzağı:
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true (!)
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false (!)⚠️ Java -128 ile 127 arasındaki Integer değerlerini cache'ler (Integer Cache). Bu aralıkta == doğru çalışır gibi görünür ama 128'den itibaren farklı nesneler oluşur.
Her zaman `equals()` kullan:
Integer a = 200;
Integer b = 200;
System.out.println(a.equals(b)); // true — güvenli3. Performans:
// KÖTÜ — her adımda autoboxing/unboxing
Long toplam = 0L;
for (int i = 0; i < 1000000; i++) {
toplam += i; // Unbox → topla → autobox (her iterasyonda!)
}
// İYİ — primitive kullan
long toplam2 = 0L;
for (int i = 0; i < 1000000; i++) {
toplam2 += i; // Saf primitive işlem
}Döngülerde ve yoğun hesaplamalarda her zaman primitive tercih et. Autoboxing/unboxing bedava değil.
Optional ile Güvenli Wrapper Kullanımı
Java 8+ ile Optional sınıfı null güvenliği sağlar:
import java.util.Optional;
// Integer null olabilir — Optional ile güvenli kullan
Integer sayi = getNullableSayi();
int deger = Optional.ofNullable(sayi).orElse(0);
// Map'ten değer okurken
Map<String, Integer> puanlar = Map.of("Ali", 85, "Veli", 92);
int aliPuani = Optional.ofNullable(puanlar.get("Ali")).orElse(0); // 85
int ayPuani = Optional.ofNullable(puanlar.get("Ayşe")).orElse(0); // 0 (yok)String'den Dönüşümler
Kullanıcı girdisi String olarak gelir, sayıya çevirmen gerekir:
String yasStr = "25";
String fiyatStr = "19.99";
String aktifStr = "true";
int yas = Integer.parseInt(yasStr);
double fiyat = Double.parseDouble(fiyatStr);
boolean aktif = Boolean.parseBoolean(aktifStr);
System.out.println(yas); // 25
System.out.println(fiyat); // 19.99
System.out.println(aktif); // true// Sayı → String (birden fazla yol)
int sayi = 42;
String s1 = String.valueOf(sayi); // En temiz yol
String s2 = Integer.toString(sayi); // Açık dönüşüm
String s3 = "" + sayi; // Kısa ama dolaylı
String s4 = String.format("%d", sayi); // FormatlaPratik Örnek: Tip Güvenli Hesaplama
public class TipGuvenliHesaplama {
public static void main(String[] args) {
// Senaryo: Öğrenci not ortalaması
int not1 = 85;
int not2 = 92;
int not3 = 78;
int toplamNot = not1 + not2 + not3;
int ogrenciSayisi = 3;
// YANLIŞ: integer division
double yanlisOrtalama = toplamNot / ogrenciSayisi;
System.out.println("Yanlış: " + yanlisOrtalama); // 85.0
// DOĞRU: cast ile
double dogruOrtalama = (double) toplamNot / ogrenciSayisi;
System.out.println("Doğru: " + dogruOrtalama); // 85.0
// (Bu örnekte tesadüfen aynı çıktı,
// ama 10+20+15 / 3 = 15.0 vs 15.0 farkı net)
// Güvenli narrowing
long uzunDeger = 42L;
if (uzunDeger >= Integer.MIN_VALUE &&
uzunDeger <= Integer.MAX_VALUE) {
int kisa = (int) uzunDeger;
System.out.println("Güvenli cast: " + kisa);
}
// String dönüşümü
String giriş = "95";
try {
int not4 = Integer.parseInt(giriş);
System.out.println("Parsed: " + not4);
} catch (NumberFormatException e) {
System.out.println("Geçersiz not!");
}
}
}Nesne Tipi Dönüşümü (Upcasting & Downcasting)
Primitive dönüşümlerin yanında, sınıf hiyerarşisinde de tür dönüşümü vardır. Bunu ileride OOP derslerinde detaylıca göreceğiz ama temel mantığını burada görelim:
// Upcasting — alt sınıf → üst sınıf (otomatik, güvenli)
Object obj = "Merhaba"; // String → Object (otomatik)
Number num = 42; // Integer → Number (autobox + upcast)
// Downcasting — üst sınıf → alt sınıf (explicit, riskli)
Object obj2 = "Merhaba";
String str = (String) obj2; // OK — gerçekten String
Object obj3 = 42; // Integer (autoboxing)
// String str2 = (String) obj3; // ClassCastException! Integer String değil
// Güvenli downcasting — instanceof ile kontrol et
if (obj3 instanceof String s) {
System.out.println(s.length());
} else {
System.out.println("String değil: " + obj3.getClass().getSimpleName());
}Tip Dönüşüm Zinciri
Bazen birden fazla dönüşüm gerekir:
// String → int → double
String str = "42";
int i = Integer.parseInt(str);
double d = i; // widening
// Kısa yol
double d2 = Integer.parseInt("42"); // parse + widening
// String → double direkt
double d3 = Double.parseDouble("3.14");
// int → String → char dizisi
int sayi = 12345;
char[] rakamlar = String.valueOf(sayi).toCharArray();
System.out.println(rakamlar[0]); // '1'
// Farklı tabanda dönüşüm
String hex = Integer.toHexString(255); // "ff"
String bin = Integer.toBinaryString(255); // "11111111"
int fromHex = Integer.parseInt("ff", 16); // 255
int fromBin = Integer.parseInt("11111111", 2); // 255Pratik Örnek: Veri Tipi Dönüştürücü Utility
public class TipDonusturucu {
// Güvenli parseInt — hata durumunda varsayılan değer döner
public static int guvenliParseInt(String str, int varsayilan) {
if (str == null || str.isBlank()) {
return varsayilan;
}
try {
return Integer.parseInt(str.strip());
} catch (NumberFormatException e) {
return varsayilan;
}
}
// Güvenli parseDouble
public static double guvenliParseDouble(String str, double varsayilan) {
if (str == null || str.isBlank()) {
return varsayilan;
}
try {
return Double.parseDouble(str.strip());
} catch (NumberFormatException e) {
return varsayilan;
}
}
// Güvenli long → int (taşma durumunda clamp)
public static int clampToInt(long deger) {
if (deger > Integer.MAX_VALUE) return Integer.MAX_VALUE;
if (deger < Integer.MIN_VALUE) return Integer.MIN_VALUE;
return (int) deger;
}
public static void main(String[] args) {
System.out.println(guvenliParseInt("42", 0)); // 42
System.out.println(guvenliParseInt("abc", 0)); // 0
System.out.println(guvenliParseInt(null, -1)); // -1
System.out.println(guvenliParseInt(" 99 ", 0)); // 99
System.out.println(clampToInt(3000000000L)); // 2147483647
System.out.println(clampToInt(42L)); // 42
}
}Bu tür utility metotlar gerçek projelerde çok sık lazım olur. Kullanıcı girdisi her zaman String gelir ve her zaman geçerli bir sayı olmayabilir.
Dönüşüm Özet Tablosu
Hangi dönüşüm güvenli, hangisi riskli? Hepsini bir arada görelim:
| Kaynak → Hedef | Tür | Otomatik? | Risk |
|---|---|---|---|
| byte → short | Widening | Evet | Yok |
| short → int | Widening | Evet | Yok |
| int → long | Widening | Evet | Yok |
| int → float | Widening | Evet | Hassasiyet kaybı olabilir |
| int → double | Widening | Evet | Yok |
| long → float | Widening | Evet | Hassasiyet kaybı! |
| long → double | Widening | Evet | Hassasiyet kaybı olabilir |
| double → int | Narrowing | Hayır | Ondalık kısım kesilir |
| long → int | Narrowing | Hayır | Taşma riski |
| int → byte | Narrowing | Hayır | Taşma riski |
| String → int | Parse | Hayır | NumberFormatException |
| int → String | Conversion | Hayır | Yok |
| Integer → int | Unboxing | Evet | NullPointerException |
| int → Integer | Autoboxing | Evet | Yok |
// Güvenli dönüşümler — her zaman çalışır
byte b = 42;
int i = b; // widening
long l = i; // widening
double d = l; // widening
// Riskli dönüşümler — kontrol et
double pi = 3.14;
int tamsayi = (int) pi; // 3 (ondalık kayıp)
long buyuk = 3000000000L;
int kucuk = (int) buyuk; // negatif sayı! (overflow)
// Tehlikeli dönüşümler — exception yakalA
String str = "abc";
// int sayi = Integer.parseInt(str); // NumberFormatException!Sık Yapılan Hatalar
1. Narrowing'de veri kaybını fark etmemek:
int buyuk = 130;
byte kucuk = (byte) buyuk; // -126! Hata yok, uyarı yok2. null unboxing:
Integer sayi = null;
int x = sayi; // NullPointerException!
// Güvenli yol
int y = (sayi != null) ? sayi : 0;
// veya Java 9+
int z = java.util.Objects.requireNonNullElse(sayi, 0);3. Integer cache tuzağı:
Integer a = 200, b = 200;
System.out.println(a == b); // false — equals() kullan!
System.out.println(a.equals(b)); // true4. double → int yuvarlamaz, keser:
System.out.println((int) 9.99); // 9, 10 değil!
System.out.println((int) -2.7); // -2, -3 değil!
System.out.println(Math.round(9.99)); // 10 — yuvarlama istiyorsan
System.out.println(Math.round(9.5)); // 10
System.out.println(Math.round(9.49)); // 95. Cast sırasını yanlış yapmak:
int a = 1_000_000;
int b = 1_000_000;
// long c = a * b; // Overflow! Çarpma int'te yapılır, sonra long'a atanır
long d = (long) a * b; // Doğru — önce a'yı long yap, sonra çarpÖzet
Widening (küçük → büyük) otomatik ve güvenlidir; narrowing (büyük → küçük) explicit cast gerektirir ve veri kaybı riski taşır
byteveshortaritmetikte otomatikint'e yükseltilir — sonucu geri atarken cast gerekirWrapper sınıflar (Integer, Double vb.) koleksiyonlar ve null değer için zorunludur
Autoboxing/unboxing otomatiktir ama null unboxing
NullPointerExceptionverir — dikkat etInteger cache (-128 ile 127) yüzünden
==bazen doğru çalışır gibi görünür — her zaman `equals()` kullanString → sayı dönüşümünde
parseInt(),parseDouble()kullan veNumberFormatException'ı yakalaCast sırasına dikkat et:
(long) a * bile(long)(a * b)çok farklı sonuç verir — cast'ı işlemden önce yap
Mülakat Soruları
S: Widening ve narrowing farkı? C: Widening küçük tipten büyüğe dönüşüm (byte→int), otomatik ve güvenli. Narrowing büyük tipten küçüğe (int→byte), explicit cast gerektirir ve veri kaybı riski taşır.
S: Autoboxing nedir? C: Java'nın primitive değeri otomatik olarak wrapper nesnesine çevirmesi. Integer x = 42 yazdığında Java Integer.valueOf(42) çağırır.
S: Integer.valueOf(127) == Integer.valueOf(127) neden true? C: Java -128 ile 127 arasındaki Integer değerlerini cache'ler. Bu aralıkta valueOf() aynı nesneyi döner. 128 ve üzerinde yeni nesne oluşturulur, bu yüzden == false döner.
S: (int) 9.99 sonucu nedir? C: 9. Narrowing cast ondalık kısmı yuvarlamaz, keser (truncation). Yuvarlama istiyorsan Math.round() kullan.
AI Asistan
Sorularını yanıtlamaya hazır