---
title: 'Genel Tipler'
source: 'https://academia.sh/tr/kurslar/java-nesne-modeli/genel-tipler'
course: 'Nesneye Dayalı Java'
language: tr
updated: '2026-08-17T18:09:41+00:00'
license: 'CC BY-SA 4.0'
---

# Genel Tipler

Tip parametresi sınıf dosyasına iki ayrı yerde yazılır: sanal makinenin okuduğu tanımda silinmiştir, imza özniteliğinde durur. Ölçüm bunun sonucunu gösterir — denetim yalnız derleme zamanındadır, iki genel tip çalışma zamanında aynı sınıftır ve ham tiple yapılan yazma okuyan yerde düşer.

Önceki ders bir kayıt için derleyicinin ürettiği üyeleri saydı: erişimciler, eşitlik, karma
ve metne çevirme kaynakta yazılmadan sınıf dosyasına girdi. Derleyici üye üretebiliyorsa,
elindeki tip bilgisiyle ne yapıyor — onu da bir yere yazıyor mu, yoksa kullanıp bırakıyor mu?

Genellikler (generics) TypeScript kursunda tip parametreleri ve kısıtlarla, ifade gücü
açısından kuruldu; bu ders onu tekrarlamaz. Buradaki soru kursun sorusudur: `Depo<Ust>`
yazımındaki `Ust` bilgisiyle kararı **kim ve ne zaman** veriyor? Yanıt bu konuda uçtadır —
kararın tamamı bildirilen tiptedir ve çalışma zamanı tipinin söyleyecek hiçbir şeyi yoktur.
Ölçüm bunun üç sonucunu gösterecek: iki ayrı tip parametresiyle yazılmış iki kap çalışma
zamanında ayırt edilebiliyor mu, denetim nereye konuyor, ve denetimi yapan taraf ortadan
kalktığında yanlış yazmanın bedelini kim ödüyor?

## Tip Parametresi Sınıf Dosyasına İki Kez Yazılır

- **GN1** — Ölçülen kaynak metni dersin içinde yazılır. Ne yazdığımızı bildiğimiz için, sınıf
  dosyasında bulunan her fark derleyicinin kararıdır.
- **GN2** — `Olcek` çekirdeği ortak tanımdan gelir. Bu ders ona tek bir ölçüm ekler: bir
  öğenin **imza özniteliğini** döndüren `imza`. Var olan ölçümlerin davranışı değişmez.
- **GN3** — Tanımlayıcı ve imza metinleri sınıf dosyasındaki ham biçimleriyle basılır;
  kısaltılmaz ve çevrilmez.

```java
// Olcek.java — ortak ölçüm çekirdeği: sınıf dosyasını derleyip okur
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.classfile.*;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

class Olcek {
    static ClassModel derle(String kaynak, String sinif) throws Exception {
        Path d = Files.createTempDirectory("olcek");
        Path k = d.resolve(sinif + ".java");
        Files.writeString(k, kaynak);
        PrintWriter bos = new PrintWriter(Writer.nullWriter());
        if (ToolProvider.findFirst("javac").orElseThrow()
                .run(bos, bos, "-d", d.toString(), k.toString()) != 0)
            throw new IllegalStateException("derlenmedi: " + sinif);
        return ClassFile.of().parse(d.resolve(sinif + ".class"));
    }

    static List<String> komutlar(String kaynak, String sinif, String yontem) throws Exception {
        List<String> adlar = new ArrayList<>();
        for (MethodModel m : derle(kaynak, sinif).methods())
            if (m.methodName().stringValue().equals(yontem))
                for (CodeElement e : m.code().orElseThrow())
                    if (e instanceof Instruction i)
                        adlar.add(i.opcode().name().toLowerCase(Locale.ROOT));
        return adlar;
    }

    static String imza(AttributedElement e) {
        return e.findAttribute(Attributes.signature())
                .map(a -> a.signature().stringValue()).orElse("-");
    }
}
```

```java
// Imza.java — tip parametresi sınıf dosyasında nerede duruyor
import java.lang.classfile.*;

public class Imza {
    static final String DEPO = """
        class Depo<T> {
            private T oge;
            void koy(T oge) { this.oge = oge; }
            T al() { return oge; }
        }
        """;

    public static void main(String[] args) throws Exception {
        ClassModel cm = Olcek.derle(DEPO, "Depo");
        System.out.printf("%-14s %-26s %s%n", "uye", "sanal makinenin gordugu", "imza ozniteligi");
        System.out.printf("%-14s %-26s %s%n", "Depo sinifi", "-", Olcek.imza(cm));
        for (FieldModel f : cm.fields())
            System.out.printf("%-14s %-26s %s%n", "alan " + f.fieldName().stringValue(),
                    f.fieldType().stringValue(), Olcek.imza(f));
        for (MethodModel m : cm.methods())
            System.out.printf("%-14s %-26s %s%n", "yontem " + m.methodName().stringValue(),
                    m.methodType().stringValue(), Olcek.imza(m));
    }
}
```

```
uye            sanal makinenin gordugu    imza ozniteligi
Depo sinifi    -                          <T:Ljava/lang/Object;>Ljava/lang/Object;
alan oge       Ljava/lang/Object;         TT;
yontem <init>  ()V                        -
yontem koy     (Ljava/lang/Object;)V      (TT;)V
yontem al      ()Ljava/lang/Object;       ()TT;
```

Orta sütun **tip silmedir** (type erasure). Kaynakta `T` yazan her yer sınıf dosyasının
tanımlayıcısında `Object` olmuştur: alan `Ljava/lang/Object;`, `koy` yöntemi bir `Object`
alır, `al` yöntemi bir `Object` döndürür. Sanal makine bir yöntemi çağırırken bu sütuna
bakar; onun için `Depo` tek parametreli sıradan bir sınıftır ve `T` diye bir şey yoktur.

Sağ sütun ise **silinmemiştir**. Sınıfın imzası `T`'nin var olduğunu ve üst sınırının
`Object` olduğunu yazıyor; alanın imzası `TT;`, yani "tip değişkeni `T`"; `koy` ile `al`
imzalarında da `T` adıyla duruyor. Kaynakta yazılan bilgi kaybolmadı, **ikinci bir yere**
yazıldı. Yapıcının imzası yok, çünkü yapıcının tanımında tip parametresi geçmiyor.

Buradan kursun sorusunun bu konudaki yanıtı çıkıyor. Sağ sütunu **yalnız derleyici** okur;
sol sütunu sanal makine yürütür. Tip parametresiyle ilgili her karar, çalışma zamanı tipinin
hiç bakmadığı bir metne dayanır.

## Denetim Okuyan Yere Konur, Yazana Konmaz

- **GN4** — Aynı yazma üç ayrı kaynakta denenir; ikisi derlenir, biri derlenmez. Derleyicinin
  ileti metni basılmaz, yalnız derlenip derlenmediği bildirilir.

```java
// Denetim.java — genel çağrı sınıf dosyasında hangi komutu bırakıyor
public class Denetim {
    static final String TIPLER = """
        class Ust { String ad() { return "ust yontem"; } }
        class Alt extends Ust { @Override String ad() { return "alt yontem"; } }
        class Depo<T> { private T oge; void koy(T o) { oge = o; } T al() { return oge; } }
        """;

    static final String KULLANIM = TIPLER + """
        class Kullanim {
            static Ust oku(Depo<Ust> d) { return d.al(); }
            @SuppressWarnings("rawtypes")
            static Object hamOku(Depo d) { return d.al(); }
            static void yaz(Depo<Ust> d, Alt a) { d.koy(a); }
        }
        """;

    static final String YAZMA =
            TIPLER + "class Yazma { static void y(%s d) { d.koy(new Alt()); } }";

    static void dene(String ad, String kaynak) {
        try {
            Olcek.derle(kaynak, "Yazma");
            System.out.printf("%-30s -> derlendi%n", ad);
        } catch (Exception e) {
            System.out.printf("%-30s -> %s%n", ad, e.getMessage());
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("okuma  Depo<Ust> : " + Olcek.komutlar(KULLANIM, "Kullanim", "oku"));
        System.out.println("okuma  ham Depo  : " + Olcek.komutlar(KULLANIM, "Kullanim", "hamOku"));
        System.out.println("yazma  Depo<Ust> : " + Olcek.komutlar(KULLANIM, "Kullanim", "yaz"));
        System.out.println();
        dene("Depo<Ust> d; d.koy(Alt)", YAZMA.formatted("Depo<Ust>"));
        dene("Depo<String> d; d.koy(Alt)", YAZMA.formatted("Depo<String>"));
        dene("Depo d (ham); d.koy(Alt)", YAZMA.formatted("Depo"));
    }
}
```

```
okuma  Depo<Ust> : [aload_0, invokevirtual, checkcast, areturn]
okuma  ham Depo  : [aload_0, invokevirtual, areturn]
yazma  Depo<Ust> : [aload_0, aload_1, invokevirtual, return]

Depo<Ust> d; d.koy(Alt)        -> derlendi
Depo<String> d; d.koy(Alt)     -> derlenmedi: Yazma
Depo d (ham); d.koy(Alt)       -> derlendi
```

İlk iki satır tek bir komutla ayrılıyor. `Depo<Ust>` üzerinden yapılan okuma **dört**
komuttur ve dördüncüsü `checkcast`; ham tip üzerinden yapılan aynı okuma **üç** komuttur ve
denetim yoktur. Çağrılan yöntem ikisinde de aynı yöntemdir — sınıf dosyasında `Object`
döndüren tek bir `al` vardır. Farkı yapan şey çağrılan taraf değil, **çağıran taraftır**:
derleyici okunan değeri bildirilen tipe uydurmak için bir denetim koyar.

Üçüncü satır dersin en sessiz ölçümüdür. Yazma **üç** komuttur ve içinde hiçbir denetim
yoktur: referansı yükle, değeri yükle, çağır. Okuma yerine denetim konur, yazma yerine
konmaz. Bunun nedeni alt tablodadır: yazma zaten derleme zamanında denetlenmiştir.
`Depo<String>` olarak adlandırılmış bir depoya `Alt` yazan kaynak **derlenmiyor**. Karar
tümüyle bildirilen tipten geliyor ve derleme durduğu için çalışma zamanına hiçbir iş
kalmıyor.

Alt tablonun üçüncü satırı ise ödenen bedeli yazıyor. Aynı depo **ham tiple** (raw type)
adlandırıldığında aynı yazma **derleniyor**. Derleyici burada bir uyarı üretir, ama uyarı
derlemeyi durdurmaz; bildirilen tip ortadan kalkınca denetleyecek taraf da kalmaz.

## Kısıt Silinen Tipi Değiştirir

- **GN5** — Beş denemenin hepsi aynı kısıtlı sınıf üzerine kurulur; yalnız denenen satır
  değişir, çevresindeki tipler sabit kalır.

```java
// Sinir.java — kısıt silinen tipi değiştirir
import java.lang.classfile.*;

public class Sinir {
    static final String SINIRLI = """
        class Ust { String ad() { return "ust yontem"; } }
        class Alt extends Ust { @Override String ad() { return "alt yontem"; } }
        class Sinirli<T extends Ust> {
            private T oge;
            void koy(T oge) { this.oge = oge; }
            T al() { return oge; }
        }
        """;

    static final String OKUMA = SINIRLI + """
        class Okuma {
            static Ust ustOku(Sinirli<Ust> d) { return d.al(); }
            static Alt altOku(Sinirli<Alt> d) { return d.al(); }
        }
        """;

    static void dene(String ad, String bildirim, String govde) {
        try {
            Olcek.derle(SINIRLI + "class Yasak" + bildirim + " { " + govde + " }", "Yasak");
            System.out.printf("%-38s -> derlendi%n", ad);
        } catch (Exception e) {
            System.out.printf("%-38s -> %s%n", ad, e.getMessage());
        }
    }

    public static void main(String[] args) throws Exception {
        ClassModel cm = Olcek.derle(SINIRLI, "Sinirli");
        System.out.printf("%-14s %-26s %s%n", "uye", "sanal makinenin gordugu", "imza ozniteligi");
        System.out.printf("%-14s %-26s %s%n", "Sinirli sinifi", "-", Olcek.imza(cm));
        for (FieldModel f : cm.fields())
            System.out.printf("%-14s %-26s %s%n", "alan " + f.fieldName().stringValue(),
                    f.fieldType().stringValue(), Olcek.imza(f));
        for (MethodModel m : cm.methods())
            if (!m.methodName().stringValue().equals("<init>"))
                System.out.printf("%-14s %-26s %s%n", "yontem " + m.methodName().stringValue(),
                        m.methodType().stringValue(), Olcek.imza(m));

        System.out.println();
        System.out.println("Sinirli<Ust> okuma : " + Olcek.komutlar(OKUMA, "Okuma", "ustOku"));
        System.out.println("Sinirli<Alt> okuma : " + Olcek.komutlar(OKUMA, "Okuma", "altOku"));

        System.out.println();
        dene("<T> icinde o.ad()", "<T>", "String s(T o) { return o.ad(); }");
        dene("<T extends Ust> icinde o.ad()", "<T extends Ust>", "String s(T o) { return o.ad(); }");
        dene("new T[3]", "<T extends Ust>", "Object[] d() { return new T[3]; }");
        dene("x instanceof Sinirli<Ust>", "",
                "boolean t(Object x) { return x instanceof Sinirli<Ust>; }");
        dene("x instanceof Sinirli<?>", "",
                "boolean t(Object x) { return x instanceof Sinirli<?>; }");
    }
}
```

```
uye            sanal makinenin gordugu    imza ozniteligi
Sinirli sinifi -                          <T:LUst;>Ljava/lang/Object;
alan oge       LUst;                      TT;
yontem koy     (LUst;)V                   (TT;)V
yontem al      ()LUst;                    ()TT;

Sinirli<Ust> okuma : [aload_0, invokevirtual, areturn]
Sinirli<Alt> okuma : [aload_0, invokevirtual, checkcast, areturn]

<T> icinde o.ad()                      -> derlenmedi: Yasak
<T extends Ust> icinde o.ad()          -> derlendi
new T[3]                               -> derlenmedi: Yasak
x instanceof Sinirli<Ust>              -> derlenmedi: Yasak
x instanceof Sinirli<?>                -> derlendi
```

Tanımlayıcı sütunu değişti: kısıtlı bir tip parametresi `Object`'e değil, **sınırına**
siliniyor. Alan `LUst;`, `koy` bir `Ust` alıyor, `al` bir `Ust` döndürüyor. İmza sütunu ise
aynı kaldı — orada hâlâ `TT;` yazıyor ve sınıf imzası sınırı da taşıyor.

Bunun ilk sonucu alt tablonun ilk iki satırında. Sınırsız bir tip parametresi üzerinden
`o.ad()` çağıran kaynak **derlenmiyor**, çünkü silinen tip `Object`'tir ve `Object` üzerinde
böyle bir yöntem yoktur. Aynı satır sınır konduğunda **derleniyor**. Kısıt bir belgelendirme
değildir; sınıf dosyasındaki tanımlayıcıyı değiştirir ve çağrının bağlanabileceği yöntem
kümesini genişletir.

İkinci sonuç okuma satırlarında ve önceki bölümün ölçümünü tamamlıyor. `Sinirli<Ust>`
üzerinden yapılan okumada `checkcast` **yok**, `Sinirli<Alt>` üzerinden yapılanda **var**.
Denetim her genel okumaya eklenmez; yalnız bildirilen tip ile silinen tip **ayrıştığında**
eklenir. İlk ölçümde `Depo<T>` `Object`'e siliniyordu ve `Ust` okuması ayrışıyordu; burada
silinen tip zaten `Ust` olduğu için eklenecek bir şey kalmıyor.

Son üç satır silmenin doğrudan yasaklarıdır. `new T[3]` derlenmiyor: bir dizi öğe tipini
kendi içinde taşır ve her yazmada onu denetler — Java Temelleri kursunda ölçülmüştü — ama
çalışma zamanında `T` diye bir tip bulunmadığı için taşınacak bir şey yoktur. `x instanceof
Sinirli<Ust>` de derlenmiyor, oysa `x instanceof Sinirli<?>` derleniyor. Sanal makineye
sorulabilecek tek soru ham sınıftır; joker karakter (wildcard) tam olarak "tip parametresini
sorma" demektir.

## İki Genel Tip Çalışma Zamanında Aynı Sınıftır

- **GN6** — Kimlik yalnız `==` ile sorulur, hiçbir kimlik sayısı basılmaz.
- **GN7** — Ham tipli yazmanın sonucu iddia olarak yazılmaz, `instanceof` ile ölçülür;
  yakalanan istisnadan yalnız sınıf adı basılır.

```java
// Silme.java — tip silme çalışma zamanında ne bırakıyor
import java.util.Arrays;

class Ust {
    String ad() { return "ust yontem"; }
}

class Alt extends Ust {
    @Override String ad() { return "alt yontem"; }
}

class Depo<T> {
    private T oge;
    void koy(T oge) { this.oge = oge; }
    T al() { return oge; }
}

public class Silme {
    static Depo<Ust> alan;

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static void main(String[] args) throws Exception {
        Depo<Ust> tasiyicilar = new Depo<>();
        Depo<String> metinler = new Depo<>();
        System.out.println("Depo<Ust> ile Depo<String> ayni sinif mi : "
                + (tasiyicilar.getClass() == metinler.getClass()));
        System.out.println("calisma zamaninda tip parametreleri      : "
                + Arrays.toString(Depo.class.getTypeParameters()));

        tasiyicilar.koy(new Alt());
        System.out.println("okunan ogenin calisma zamani tipi        : "
                + tasiyicilar.al().getClass().getSimpleName());

        Depo ham = tasiyicilar;
        ham.koy("kuzey");
        System.out.println("ham tiple yazma durduruldu mu            : "
                + (tasiyicilar.al() instanceof Ust ? "evet" : "hayir"));
        try {
            Ust u = tasiyicilar.al();
            System.out.println("okuma sonucu                             : " + u.ad());
        } catch (ClassCastException e) {
            System.out.println("hata yazan yerde degil, OKUYAN yerde     : "
                    + e.getClass().getSimpleName());
        }
        System.out.println("alanin imza ozniteliginden okunan tipi   : "
                + Silme.class.getDeclaredField("alan").getGenericType());
    }
}
```

```
Depo<Ust> ile Depo<String> ayni sinif mi : true
calisma zamaninda tip parametreleri      : [T]
okunan ogenin calisma zamani tipi        : Alt
ham tiple yazma durduruldu mu            : hayir
hata yazan yerde degil, OKUYAN yerde     : ClassCastException
alanin imza ozniteliginden okunan tipi   : Depo<Ust>
```

İlk iki satır tanımlayıcı sütununun sonucudur. `Depo<Ust>` ile `Depo<String>` çalışma
zamanında **aynı** sınıftır ve o sınıfın tip parametresi yalnız `T` adıyla, bir değeri
olmadan durur. Kaynakta iki ayrı tip yazılmıştı; öbekte tek bir sınıf var.

Üçüncü satır ayrımı keskinleştiriyor. Depodan okunan nesnenin çalışma zamanı tipi `Alt` —
yani nesnenin kendi sınıfı bilinir ve gizli değildir. Bilinmeyen şey **kabın** ne tuttuğudur.
Nesnenin tipi çalışma zamanında vardır, kabın tip parametresi yoktur.

Dördüncü ve beşinci satırlar bedeli ölçüyor. Ham tiple yazılan dizgi hiçbir yerde
durdurulmadı: depo onu kabul etti ve depodaki öğe artık bir `Ust` değil. Hata, yazmanın
yapıldığı satırda değil, **okumanın yapıldığı** satırda düştü — çünkü tek denetim oradaki
`checkcast` komutudur. Yanlış tarafa bakan bir programcının ödediği bedel budur: kusuru
üreten satır ile kusurun göründüğü satır ayrı yerlerdedir, ve aradaki uzaklık programın
uzunluğu kadar olabilir.

## Silinen Bilgi Değil, Denetimdir

Son satır dersin sınırlayıcı ölçümüdür ve tezi tersine çeviriyor. `Depo<Ust>` diye
bildirilmiş bir alanın genel tipi çalışma zamanında **okunabiliyor**: `Depo<Ust>`. Bilgi
silinmemiştir, imza özniteliğinde durmaktadır ve oradan geri alınabilir.

O halde silinen nedir? İlk ölçümün iki sütunu yanıtı veriyor. Sanal makinenin yürüttüğü
tanımlayıcıda `Object` yazar; sanal makine bir çağrıyı denetlerken imza özniteliğine
bakmaz. Tip bilgisi sınıf dosyasında **veri** olarak durur, ama **denetim** olarak
kullanılmaz. Bu yüzden "tip silme" adı, bilginin değil, o bilgiye dayanan denetimin
kaybolmasını anlatır. Bir alanın imzasını okumakla bir yazmanın engellenmesi ayrı şeylerdir:
birincisi bir metni geri okumaktır, ikincisi ise çalışma zamanında hiç yapılmayan bir iştir.

## Özet

- Tip parametresi sınıf dosyasına iki ayrı yere yazılır: sanal makinenin okuduğu
  tanımlayıcıda `Object` olarak silinmiş, imza özniteliğinde `T` adıyla yazılı durur.
- Genel bir okuma çağrısı sınıf dosyasında bir `checkcast` komutu ekler; aynı okuma ham tip
  üzerinden yapıldığında o komut yoktur. Denetim çağrılan tarafta değil, çağıran tarafta
  bulunur.
- Genel bir yazma çağrısı hiçbir denetim komutu bırakmaz, çünkü yazma derleme zamanında
  denetlenmiştir: `Depo<String>` deposuna `Alt` yazan kaynak derlenmez.
- Kısıtlı bir tip parametresi `Object`'e değil sınırına silinir; bu hem `T` üzerinden yöntem
  çağrılmasını açar hem de bildirilen tiple silinen tip çakıştığında `checkcast` komutunu
  ortadan kaldırır.
- Çalışma zamanında `Depo<Ust>` ile `Depo<String>` aynı sınıftır; ham tiple yapılan yazma
  durdurulmaz ve hata yazan yerde değil, okuyan yerde düşer.
- Sınırlayıcı ölçüm: alanın genel tipi çalışma zamanında imza özniteliğinden geri
  okunabilir. Silinen şey bilgi değil, o bilgiye dayanan denetimdir.

## Sonraki Adım

Bu derste bir depo tek bir tiple adlandırıldı ve o tipe yazmakla o tipten okumak arasında
denetim bakımından bir fark çıktı: okumaya bir komut eklendi, yazmaya eklenmedi. Aynı fark
tip düzeyinde de sorulabilir. `Depo<Ust>` yerine "`Ust` ya da altındaki herhangi bir tipi
tutan depo" yazmak istersek, o depodan okumak ile ona yazmak aynı ölçüde açık kalır mı?
Sıradaki ders dört ayrı kap biçimini iki işlemle deneyip kaç çiftin derlendiğini sayar.
