Java - Constructor ,this-this() ve super-super() Farkları

Java - Constructor ,this-this() ve super-super() Farkları

Arkadaşlar merhaba,

Bu yazımızda java'da kurucu yani constructor methoduna dilimiz döndüğünce nedir ne değildir açıklamaya çalışacağım.

  • Nesne oluşturulurken çağrılan metota kurucu (constructor method) denir.

  • Kurucu metotlar sadece nesne oluştururken ve new anahtar sözcüğü ile çağrılırlar ve nesnenin ilk durumunu belirlemek (initialization) için kullanılan özel bir metotdur.

  • Java'da kurucu çağrısı yapmadan nesnesi oluşturulabilen çok az tip vardır. Bu tiplerden bazıları;

String dizi (array), basit tiplerin nesne halleri(wrappers) vb.

Yapıcı hakkında dikkat edilmesi gereken iki önemli nokta vardır.

  • Yapıcının adı sınıfın adıyla eşleşir ve herhangi bir dönüş türü yoktur.

  • Method parametreleri gibi constructor parametrelerinde class , primitive type, array , generics kullanılabilir.

Fakat "var" kullanılamaz.

public Bonobo(var food) { // DOES NOT COMPILE }
  • Her kurucunun benzersiz bir imzası olduğu sürece, bir sınıfın birden çok kurucusu olabilir. Bu durumda, bu, yapıcı parametrelerinin farklı olması gerektiği anlamına gelir.

  • Aynı ada ancak farklı imzalara sahip methodlar gibi, farklı imzalara sahip kurucu methodlar constructor overloading olarak adlandırılır.

new ClassName()

ile sınıfın yeni bir instance'si oluşturulduğunda constructor çağırılır.

Constructors, yeni bir nesne oluştururken kullanılır. Bu işleme, sınıfın yeni bir instance'i(örneğini) oluşturduğu için instantiation adı verilir.

  • Java, new anahtar sözcüğünü gördüğünde, yeni nesne için bellek ayırır.Ardından, eşleşen imzaya(signature) sahip bir kurucu arar ve onu çağırır.
  • Constructor tüm field'lar ve instance variable'lar çalıştırıldıktan sonra çalışır.

  • Java'daki her sınıfın, kodlasanız da kodlasanız da bir yapıcısı vardır.Sınıfa herhangi bir kurucu eklemezseniz, Java sizin için herhangi bir parametre olmadan bir tane oluşturacaktır.

Bu, derleme adımı sırasında gerçekleşir. .Java uzantılı dosyaya bakarsanız, yapıcı yine eksik olacaktır.Yalnızca .class uzantısına sahip derlenmiş dosyada görünür.

public Bunny() {
        System.out.println("constructor");
    }
public bunny() { } 
// DOES NOT COMPILE  küçük yazıldığı için sınıf adı ile eşleşmez
public void Bunny() {
 // void dönüş türüne sahip olduğu için yapıcı method olamaz.
}
Park p = new Park();
public class Person {
   int age = 20;  // initialize on line
   String name;

   public Person() {
       name = "Umut";  // initialize in constructor
   }
}

Bir constructor’un amacı, field'ları başlatmaktır.

Field'ları başlatmanın başka bir yolu, bunu doğrudan bildirildikleri satırda yapmaktır. Üstteki örnekte her iki yaklaşımı da gösterir.

Ayrıca sınıfa bir constructor eklenmese dahi java derleme zamanın da aşağıdaki şekilde ekleyecektir.

public class Person(){  
      public Person(){
          super();
      }
}

Default Constructor

  • Yalnızca Person sınıfı default argümansız bir kurucu alır.

    Kodlanmış bir constructor’a sahip değildir,bu nedenle Java varsayılan bir argümansız constructor oluşturur.

  • Person2 ve Person3'ün zaten constructor metotları mevcuttur..

  • Person4'ün ise private bir constructor methodu vardır.

Bu üç sınıfın tanımlı bir constructor methodu olduğundan default argümansız bir constructor(no-arg constructor) method eklenmez.

public class Person {
}

class Person2 {
    public Person2() {}
}

class Person3 {
    public Person3(boolean b) {}
}

 class Person4 {
    private Person4() {}
}

Private Constructor Oluşturma

Person4 person4 = new Person4();  // does not compile

Bir sınıfta yalnızca private constructor'a sahip olmak, derleyiciye varsayılan bir argümansız kurucu sağlamamasını söyler.

Ayrıca diğer sınıfların da bu sınıfı başlatmasını da engeller.

Sadece sınıfın kendisinde tanımlanan bir iç sınıf onu miras alabilir.Bir iç sınıf private bir kurucuya erişimi olan ve super()'i çağırabilen tek sınıftır. Diğer üst düzey sınıflar böyle bir sınıfı miras(extend)alamaz.

Bu, bir sınıf yalnızca statik metotlara sahip olduğunda veya geliştirici, sınıfın yeni örneklerini (instance) oluşturmak için tüm kullanımlar üzerinde tam denetime sahip olmak istediğinde kullanışlıdır.

Main methodu da dahil olmak üzere sınıftaki statik methodların, private constructor'da dahil olmak üzere private üyelere de erişebilir.

this() ile Parametreli Constructor Çağırma

Yapıcılar (Constructor), yalnızca constructor adından önce new yazılarak çağrılabilir.

 public Person(int age) {

       Person(age, "Umut"); // does not compile
}
public class Person {

    private String name;

    private int age;

    public Person(int age, String name) { // first
        this.age = age;
        this.name = name;
    }

  public Person(int age){ // second
      this.age = age;
      name = "Umut";
   }

}

this.age her iki kurucuda da aynı şekilde iki kez atandığından, yineleme vardır.Örneğin, sadece bir tane yerine this.age gibi ayarlanmış 20 değişkenimiz olduğunu hayal edin !

Şimdi ise ilk kurucunun ikinci kurucuyu iki parametre ile nasıl çağırılabileceğine göz atalım.

public Person(int age, String name) {
            new Person(age, "Umut");   // Compiles, but incorrect
}

Bu kullanım derleme yapar. Yine de istediğimizi yapmıyor. Bu constructor çağrıldığında, varsayılan yaş ve isime sahip yeni bir nesne oluşturur.

Ardından istenen yaş ve isimde farklı bir nesne oluşturur ve yeni nesneyi yok sayar. Bu şekilde, biri oluşturulduktan sonra atılan iki nesne elde ederiz. İstediğimiz bu değil.

public Person(int age){
    this(age , "Umut");  
  }

Şimdi Java, beklendiği gibi yaş ve isim olarak iki parametre alan constructor'ı çağırıyor.

this() işlevini çağırmanın bilmeniz gereken özel bir kuralı vardır.

this'i kullanmayı seçerseniz, this() çağrısı yapıcıdaki ilk ifade olmalıdır.Bunun olumsuz yanı ise,herhangi bir kurucuda this() öğesine yalnızca bir çağrı olabileceğidir.

this ve this() Farkı

Derleyici, bu kurucunun kendisini sonsuz olarak çağırdığını algılayabilir. Bu kod hiçbir zaman sonlandırılamayacağı için derleyici durur ve bunu bir hata olarak bildirir.

public class Person {
    public Person(int age) {
        this(5); // DOES NOT COMPILE
    }
}

Aynı şekilde, bunlar da derlenmez:

public Person() {
        this(5); // DOES NOT COMPILE
}
public Person() {
        this(); // DOES NOT COMPILE
}

Bu örnekte, yapıcılar birbirini çağırır ve süreç sonsuza kadar devam eder.Derleyici bunu algılayabildiğinden, bunu bir hata olarak bildirir.

Aynı anahtar kelimeyi kullanmasına rağmen, this ve this() çok farklıdır.

Birinci this, sınıfın bir instance'na atıfta bulunurken,

İkinci this() sınıf içindeki bir constructor çağrısına atıfta bulunur.

super() ile Üst Kurucuyu Çağırma

public class Animal {
    protected int age;
    protected String name;

    public Animal(int age, String name) {
        super(); // java.lang.Object yapıcının ilk ifadesi,
        // herhangi bir üst sınıf devralmadığı için Java.lang.Object içinde tanımlanan ve hiçbir argüman almayan üst yapıcıya yapılan bir çağrıdır.

        this.age = age;
        this.name = name;
    }

    public static void main(String[] args) {
        System.out.println("Animal is created");
    }

    public Animal(int age) {
        super();
        this.age = age;
        this.name = null;
    }

}

Java'da, her kurucunun ilk ifadesi, ya this() kullanılarak sınıf içindeki başka bir kurucuya yapılan bir çağrıdır ya da doğrudan üst sınıftaki bir kurucuya super() kullanılarak yapılan bir çağrıdır.

  • Bir üst yapıcı argüman alırsa super() çağrısı da argümanları alır.
public class Zebra extends Animal {

    public Zebra(int age) {
        super(age); // Animal'daki constructor'ı ifade eder
    }

}

ilk kurucunun ilk ifadesi, Animal'ın kurucusuna yapılan ve tek bir argüman alan bir çağrıdır.

Zebra sınıfı ayrıca super()'i çağırmayan, bunun yerine this(4) kullanarak Zebra sınıfı içindeki diğer kurucuyu çağıran ikinci bir argümansız kurucu içerir.

public Zebra() {
        this(4);  // Zebra'daki yapıcıyı int argümanıyla ifade eder.
}

this() ve super() kurucuda ilk satırda kullanılmalıdırlar ve sadece bir kez çağırılabilirler.

public class Zoo {

    public Zoo() {
        System.out.println("Zoo created");
        super(); // DOES NOT COMPILE
  }

    public Zoo() {
        super();
        System.out.println("Zoo created");
        // super(); // does not compile
    }
}
public class Gorilla extends Animal {

    public Gorilla(int age) {
        super(age, "Gorilla");
        super.age = age; // üst sınıftaki üyesini çağırır.
    }

    public static void main(String[] args) {
        Gorilla g = new Gorilla();
        System.out.println("Gorilla is created : " + g.age);
        System.out.println("Gorilla is created : " + g.name);
    }

}
  • Birinci alt kurucu bir argüman alır, age ve iki argüman alan, age ve name olan ana kurucuyu çağırır.
  • Alt kurucuların eşleşen üst kurucuları çağırmasının gerekmediğine dikkat edin. Üst kurucuya uygun giriş parametreleri sağlandığı sürece herhangi bir geçerli üst kurucu kabul edilebilir.

İkinci alt kurucu hiçbir argüman almaz ve bir argüman alan üst(parent) kurucuyu çağırır.

 public Gorilla() {
        super(5);
}

Bir sınıfın kalıtım yolu ile birden fazla özelliğini devraldığı sınıflar olabilir.Bu durumda super() ile direkt olarak kalıtım yaptığı bir üst sınıfa erişebilir.

public class Elephant extends Mammal {}

public class AfricanElephant extends Elephant {}

Bu örnekte, AfricanElephant sınıfı içinde super() çağrısı her zaman Elephant sınıfına atıfta bulunur ve asla Mammal sınıfına atıfta bulunmaz.

super ve super() Farkı

Birincisi, super, parent sınıfın üyelerine başvurmak için kullanılırken, super() bir üst constructor'ı çağırır.

Constructor ve Final Fields

Final olarak işaretlenen instance variable'lar bildirildikleri satırda veya instance initializer'de değer ataması yapılabilir.

Diğer final değişkenler gibi değer atandıktan sonra değiştirilmez.

Değer ataması yapılabilecek bir yer daha vardır o da kurucu methodlardır.

Constructor (kurucu) başlatma sürecinin bir parçasıdır , bu nedenle içinde final instance variable'lar atanmasına izin verilir.

Constructor tamamlandığında , tüm final instance variabler'lara bir değer atanmalıdır.

Bunlara yalnızca bir kez değer atanmalıdır ve değer atanamaması,Constructor'da bir derleyici hatası olarak kabul edilir.

private final int age;

private final String name = "Umut Çakmak";

private final String job;

{
  age = 29;
}


public Person (){
    job = "Software Developer";
}

final instance variable yalnızca bir kez değer atanabilmesine rağmen, her kurucu atama açısından bağımsız olarak değerlendirilir.

public Person (){
    job = "Software Developer";
}

public Person (String job){
   this.job = "Software Developer";
}
public class Person {

    private final String name;
    private final int age;

    {
        this.age = 29;
    }

    public Person(String age){
        this.age = age;
    }

    public Person(){
        this.name = name; // name için hiçbir değer atanmadığı için hata verir.
        this.age = 30; // zaten değer atandı.
    }

}

Peki bir kurucu aynı sınıfta başka bir kurucu çağırdığında final instance variable ne olacak?

Her final instance variable'a tam olarak bir kez bir değer atandığından emin olarak, yapıcı mantık yolunu dikkatlice izlemeniz gerekir.

public Person() {
    this(null);
}

Bu kurucu herhangi bir final instance variable'a herhangi bir atama gerçekleştirmez, ancak Person(String) constructor'unu sorunsuz bir şekilde derler.

Değişkenin bir nesne değeri olması gerekmediğini göstermek için burada null kullanıyoruz. Açıkça ayarlandıkları sürece, final instance variable null bir değer atayabiliriz.

Yine detaylı olarak java dökümantasyonu veya aşağıdaki linkten farklı örneklere bakabilirsiniz.

Umarım faydalı olmuştur.

Görüşmek üzere.