29 Haziran 2016 Çarşamba

Generic yapılar, kalıtım ve alttipler

Bilindiği gibi, tip uyumlu olması şartıyla tipleri farklı olan nesneler birbirine atanabilmektedir. Örneğin Integer  tipindeki bir nesneyi Object tipindeki bir nesneye, Object tipi Integer tipinin bir süper tipi olduğu için rahatlıkla atanabilir.
Object someObject = new Object();
Integer
someInteger = new Integer(10);
someObject = someInteger;   // OK

Nesne yönelimli programlama terminolojisinde buna “… bir … dir” (is a) ilişkisi denir. Yukarıdaki örnek doğrudur çünkü Integer bir Object dir.  Ayrıca Integer bir Number olduğundan aşağıdaki kod da geçerlidir.
public void someMethod(Number n) { /* ... */ }



someMethod(new Integer(10));   // OK

someMethod(new Double(10.1));   // OK

Aynı durum Generic yapılar için de geçerlidir. Number tipinde bir parametreye sahip bir Generic yapıya onun alt sınıfları parametre olarak geçebilir. Aşağıdaki kod buna örnektir:
Box<Number> box = new Box<Number>();

box.add(new Integer(10));   // OK

box.add(new Double(10.1));  // OK

Yukarıdaki kod doğrudur çünkü Integer ve Double bir Number dir. Şimdi aşağıdaki kodu inceleyelim:
public void boxTest(Box<Number> n) { /* ... */ }

Burada boxTest isimli metod sizce hangi türleri argüman olarak kabul etmektedir? Tabi direk olarak gözüken Box<Number> tipindeki argümanları kabul ettiğidir. Peki bunun anlamı nedir? Beklenildiği gibi Box<Integer> ve Box<Double> tipleri argüman olarak kabul edilecek mi? Sorunun cevaı hayırdır. Çünkü Box<Integer> ve Box<Double>, Box<Number> tipinin bir alt tipi değildirler.
Bu ilk Generic kavramı ile tanışıldığında hep yanlış anlaşılan bir durumdur ve aslında öğrenilmesi elzem bir durumdur.



Not: Verilen iki ayrı A ve B tipi (örneğin Number ve Integer) için, A ve B nin aralarında ilişki olup olmadığına bakılmaksızın MyClass<A> ve MyClass<B> aralarında Object tipi dışında ilişki olmayan iki ayrı ilişkisiz tiptir.


Bu durumu aşmak için Wilcards (joker tipler) kavramı ortaya çıkmıştır. Bu kavramı bir sonraki yazıda açıklamaya çalışacağım.

Java Generic yapılar: Sınırlı tip parametresi (bounded type parameters)

Generic yapılara giriş yaptıktan sonra şimdi de sınırlı tip parametresi (bounded type parameters) nedir ona değinmek istiyorum. Bildiğiniz üzere Generic yapılar ile bir sınıf ya da metodu parametreleştirebiliyorduk. Sınırlı tip parametreleri ise adı üstünde bu parametreleştirmeye bir sınır koymaya yaramakta. Bir örnekle açıklamak yine en iyisi:
public abstract class Personel {
   
public abstract double maasHesapla();
   
public abstract void bilgiGoster();
}

public class Ogretmen extends Personel {

    private double maas;

    private int gorevYili;



    public Ogretmen(double maas, int gorevYili) {

        this.maas = maas;

        this.gorevYili = gorevYili;

    }



    @Override

    public double maasHesapla() {



        if(gorevYili > 20) {

            return maas * 1.5;

        }

        else if(gorevYili > 10){

            return maas * 1.2;

        }

            return maas;

    }



    @Override

    public void bilgiGoster() {

        System.out.println("Öğretmen maaşı: " + this.maasHesapla());

    }

}

public class Mudur extends Personel{

    private double maas;

    private int gorevYili;



    public Mudur(double maas, int gorevYili) {

        this.maas = maas;

        this.gorevYili = gorevYili;

    }



    @Override

    public double maasHesapla() {

        if(gorevYili > 20) {

            return maas * 1.8;

        }

        else if(gorevYili > 10){

            return maas * 1.4;

        }

        return maas * 1.2;

    }



    @Override

    public void bilgiGoster() {

        System.out.println("Müdür maaşı: " + this.maasHesapla());

    }

}

public class Main {



    public static void main(String[] args) {

        Mudur mdr = new Mudur(11.5, 10);

        Ogretmen ogr = new Ogretmen(10.5, 15);



        Personel prsn = maasKarsilastir(mdr, ogr);

        prsn.bilgiGoster();

    }



    public static <E extends Personel> E maasKarsilastir(E personel1, E personel2){

        if(personel1.maasHesapla() > personel2.maasHesapla()){

            return personel1;

        }

        else{

            return personel2;

        }

    }

}

Yukarıdaki örnekte bir Personel isminde soyut sınıf oluşturdum ve bu sınıftan Ogretmen ve Mudur sınıflarını türettim. Burada konumuzla ilgili dikkat edilmesi gereken nokta Main sınıfı içinde tanımladığım maasKarsilastir() metodu. Dikkat edilirse bu metoddaki Generic yapıyı <E extends Personel> şeklinde tanımladım. Burada extends anahtar kelimesi parametre tipini Personel tipi ile sınırlamamızı sağladı. Bu sayede E tipindeki personel1 argümanı Personel sınıfına ait maasHesapla() metoduna erişim sağlayabildi.
Personel prsn = maasKarsilastir(mdr, ogr);


Bu kod ile maasKarsilastir metodunun dönüş tipi Personel tipinde ilk argüman Mudur tipinde, ikinci argüman ise Ogretmen tipinde seçtim. Kod hata vermeden derlenecektir çünkü herbir argüman Personel sınıfından türediği için aslında bir Personel tipidir.

27 Haziran 2016 Pazartesi

Java Generic Yapısı

Generic yapılar Java 5 ile dile eklenen çok önemli bir özelliktir. Bu yapılar derleme zamanı tip güvenliğini sağlamak ve çalışma zamanında geliştiricilerin başına sıkça gelen ClassCastException hata riskini ortadan kaldırmak için Java diline eklenmiştir.
Örneğin, String tipinde bir HashSet sadece String nesnesini içerecek ve eğer Integer tipinde veya diğer bir tipte nesne eklenmek istendiğinde derleyici buna müdahele edecektir. Bu senaryoda Java 5 öncesinde derleyici herhangi bir hata vermeden kodu derleyecek fakat çalışma zamanında program hata verecektir.Bu yüzden Generic yapılar Java’ya sağlamlık ve tip güvenliği sağlamaktadır. Bunun yanında bir sürü temelde aynı işi yapan metod yazmak yerine Generic yapıyı kullanarak kodunuzu yeniden kullanım açısından zenginleştirmiş olursunuz.

Generic yapılar Java dilinde sınıflara, arayüzlere ve metodlara uygulanabilmektedir ve Java Dil Spesifikasyonunda (Java Language Specification) parametreleştirilmiş veri tipi (parameterized types) olarak belirlenmiştir. Parametreleştirilmiş veri tipi herhangi bir veri tipini parametre olarak kabul eden tip demektir kısaca.

Bir Generic yapı köşelil parantezler < >  içine formal tip parametreleri (formal type parameter) yazılarak tanımlanır. Şimdi formal tip parametrelerine neden ihtiyaç duyulduğunu ve ne işe yaradıklarına değinelim. Aşağıdaki kod parçasını inceleyelim:

static void genericOlmayanMetod(Object k, Object[] dizi){
   
}

Bu kodda k ve dizi herhangi bir tipte olabilmeleri ile Generic yapılara benzerdirler fakat k ile dizi arasında herhangi bir ilişkiyi göstermek söz konusu değildir. Yan parametreler aynı tipte ise k ile dizinin birbiri ile aynı tipte olabilmelerini sağlayan herhangi bir kısıtlama söz konusu değildir. Dolayısıyla bu kod hataya son derece açık bir koddur. İşte bu durumdaki problemleri aşmak ve parametreleri birbirine bağlamak için formal tip parametreleri kullanılır.  Aşağıdaki kodu inceleyelim:

static <E> void genericMetod(E k, E[] dizi){

    

}

Yukarıda tanımlanan formal tip parametresi ile k ile dizi herhangi bir tipte olabilirler ama aynı tipte olmak zorundadırlar.

Generic Sınıfılar

Bir Generic aşağıdaki gibi tanımlanır:

public class Personel<T>  {



}

Yukarıda değindiğimiz gibi T bir formal parametredir ve Personel sınıfı kullanıldığında bu parametre belirlenecektir. Mesela Personel sınıfını String tipi ile kullanalım. Burada String tipi gerçek tip parametresi olmaktadır.

Personel<String> prsnl;



Dikkat: Gerçek tip parametreleri referans tipinde olmak zorundadır. İlkel (primitive) veri tipleri gerçek tip parametresi olamazlar:

Personel< int> prsnl;
 
Bu kullanım yanlıştır. Bunun yerine şu şekilde kullanım doğrudur:
 
Personel<Integer> prsnl;

Bir Generic sınıf birden fazla formal tip parametresi ile tanımlanabilir:

public class Kredi<T, R>    {



}

Şimdi bu formal tip parametrelerini kullanarak bir sınıf tanımlayalım:

public class Node<T> {

    private T data;

    public Node next;



    public Node(T data) {

        this.data = data;

        next = null;

    }



    T getData(){

        return data;

    }

}

Node sınıfı T formal parametresi ile tanımlanmış ve bu T parametresi ile data isminde bir alan (field) oluşturulmuş, yapıcı(constructor) T tipindeki bu alanı parametre olarak almış ve bu data alanı getData ile elde edilmiştir.

Şimdi bu sınıftan bir nesne nasıl üretiriz ona bakalım:

Node<Integer> nd = new Node<Integer>(120);

Bu nesneyi oluşturuken sağ  taraf değerinde formal tip parametresini yazmak zorunda değilsiniz:

Node<Integer> nd = new Node<>(120);
 
Yukarıdaki kod da herhangi bir hata olmaksızın çalışacaktır.
 
Şimdi de data alanına nasıl erişeceğimize bakalım:
 
int data1 = nd.getData();
 
Görüldüğü gibi T tipinde geri dönüş değerine sahip getData() metodunun değeri, T tipinin Integer tipi olarak 
belirlenmesi ile, Integer tipinde bir değişkenene atanmıştır. Ayrıca geri dönüş değerini ilkel olarak int tipindeki
 bir değişkene atayabildiğimize de dikkat ediniz.
 
 
 
Generic Metodlar:
 
Generic metodları bir örnek üzerinde anlatmak en iyisi olacak:
 
package org.algoritma;



public class Main {

    public static void main(String[] args) {

        Integer[] iDizi = {1, 5, 6};



        Double[] dDizi = {1.5, 2.3, 3.3};



        String[] sDizi = {"Algoritma", ".org"};

        

        integerDiziGoster(iDizi);

        doubleDiziGoster(dDizi);

        stringDiziGoster(sDizi);

    }



    public static void integerDiziGoster(Integer[] dizi){

        for (Integer t : dizi) {

            System.out.println(t);

        }

    }



    public static void doubleDiziGoster(Double[] dizi){

        for (Double t : dizi) {

            System.out.println(t);

        }

    }



    public static void stringDiziGoster(String[] dizi){

        for (String t : dizi) {

            System.out.println(t);

        }

    }



}
 
Yukarıdaki örnekte birbirine benzeyen  üç adet metod görüyorsunuz. Makalenin başında Generic yapıların bir faydasının da yeniden yazılabilirliği güçlendirme olduğunu söylemiştim. Şimdi bu kodu Generic yapılar ile yeniden yazalım:

package org.algoritma;



public class Main {

    public static void main(String[] args) {

        Integer[] iDizi = {1, 5, 6};



        Double[] dDizi = {1.5, 2.3, 3.3};



        String[] sDizi = {"Algoritma", ".org"};



        diziGoster(iDizi);

        diziGoster(dDizi);

        diziGoster(sDizi);

    }



    public static <T> void diziGoster(T[] dizi){

        for (T t : dizi) {

            System.out.println(t);

        }

    }



}

Görüldüğü gibi üç adet metod yazmak yerine Generic tek bir metod yazarak aynı işi yapan farklı parametreli metodları tekrar tekrar tanımlamak zorunda kalmadık.

Şimdiye kadar yazdıklarımı özet olarak yeniden yazıp bu makaleyi sonlandırmak istiyorum. Sınırlı tip parametrelerini (Bounded Type Parameters) ve joker yapılarını (wildcards) bir başka makalede yazacağım.

Generic yapıların faydalarını şöyle özetleyebiliriz:

Tip Güvenliği:

Generic yapıların en büyük artısı tip güvenliği sağlamasıdır. JDK 1.5 den önce Collections sınıfı tip güvensiz  bir yapı sunuyordu bize. Çünkü bu sınıfın elemanları Object sınıfından değişkenleri kabul ediyor eğer istenilen tipte bir değişken olmasa bile derleyici herhangi bir hata vermiyordu. İşte Generic yapılar sayesinde derleme zamanında tip kontrolü yapar hale geldik. Örnek olarak aşağıdaki koda bakalım:

ArrayList<Node> nodeList = new ArrayList<Node>();

nodeList.add(5); //derleme hatası ,int kabul edilemez

Tip dönüştürme zorunluluğunu ortadan kaldırması:

Kısaca casting denilen tip dönüştürme zorunluluğu Generic yapılar kullanılarak ortadan kaldırılabilir. Aşağıdaki kodu inceleyelim:

List maddeler = new ArrayList();

maddeler.add("şeker");

String madde = (String) maddeler.get(0); //casting gerekli



List<String> maddeler = new ArrayList();

maddeler.add("un");

String madde = maddeler.get(0); //casting gerekli değil



20 Haziran 2016 Pazartesi

Java dilinde float ve double veritipleri

Java’da float ve double veritiplerinin ikisi de ondalıklı sayıları temsil etmede kullanılsa da double veritipi float veritipine göre daha hassastır. Şimdi bu iki veritpinin benzerliklerini ve farklılıklarını yazalım:

Benzerlikler:

1-      İki veritipi de yaklaşık sonuç verirler, yani ondalıklı sayıyı tam olarak tutamazlar.
2-      Mantıksal operatölerle karşılaştırma yapılacaksa == veya != operatörleri yerine  < veya > operatörleri kullanılmasında fayda vardır.  Çünkü dediğim gibi bu tipler kesin sayı tutamazlar.

Farklılıklar:

1-      double veritipi float tipine göre daha fazla alan kaplar. double 64 bit alana ihtiyaç duyarken float 32 bit alana ihtiyaç duyar.
2-      flolat bu 32 biti şöyle paylaşır: 1 bit işaret, 8 bit exponent (üs) ve 23 bit mantis için. Double ise 64 biti şöyle paylaşır: 1 bit işaret, 11 bit exponent ve 52 bit mantis için.
3-      Varsayılan olarak Java’da ondalıklı bir sayı double olarak saklanır. Eğer ondalıklı sayıyı float olarak saklamak istiyorsak sayının sonuna F veya f yazılmalıdır. Eğer ondalık bir sayı flolat olarak yazılıp bu sayının sonuna F veya f yazılmazsa derleyici hatası alırız.

public static final float PI = 3.14; //derleme zamanı hatası

Bu hatayı düzeltmek için ya casting işlemi ya da f eki kullanılmalıdır.

public static final float PI = (float)3.14;

public static final float PI = 3.14f;

Java uygulamalarında en sık karşılaşılan Exception tipleri

Takipi isimli bir raporlama servisi 1000 üzeri uygulama üzerinde gerçekleştirdiği istatistik sonucu en sık karşılaşılan Exception tiplerini listelemiş. İşte o liste:


Java isimlendirme Geleneği

Java yazım geleneği sınıfları, değişkenleri, metodları vs. isimlendirmek için yıllardır Java geliştiricileri arasında oluşan bir yazım kuralıdır. Bu, adı üstünde bir gelenektir ki bunu uygulamamak herhangi bir derleyici hatasına yol açmaz.  Bu geleneği sürdürmek diğer kişilerin kodlarını kolayca anlamanıza ve dolayısıyla sizin kodlarınızın da  diğer geliştiriceler arasında kolayca anlaşılmasını sağlar. Şimdi bu geleneğin kurallarını bir tablo halinde verelim.

İsim
   Gelenek
class ismi
Her kelime büyük harfle başlar Örnek: Personel, UyeListesi
interface ismi
Class isimlendirmesi ile aynıdır
metod ismi
İlk kelime küçük harfle başlar sonrakiler büyük harfle Örnek: isimGetir()
değişken ismi
Metod isimlendirmesi ile aynıdır
package ismi
Kelimeler küçük harfle yazılır Örnek: com.algoritma
sabit değerler
Tüm harfler büyük yazılır Örnek: PI, DOSYAYOLU

Java geleneğinde class, interface, metod ve değişken isimlendirmesinde CamelCase adı verilen yöntem kullanılır. Örnekler: dosyaSil(), ArabaSinifi, personelDizisi…

Java'da == ile equals arasındaki fark nedir?

Farklılık 1:

== ile iki ilkel (primitive) tipi karşılaştırabiliriz fakat equals() ile bunu yapamayız:

public static void main(String[] args) {
    int x = 1;
    int y = 1;
    System.out.println(x == y);//derlenir ve true yazar           
    System.out.println(x.equals(y)); //derleyici hatası verir
 }

Farklılık 2:

== ile equals() metodunu nesneleri karşılaştırmada kullanabiliriz fakat == nesnelerin aynı yeri gösterip göstermediğini kontrol etmede kullanılırken equals() metodu override edilerek niteliksel olarak eşitlik kontrolü yapılmasında kullanılabilir.

public static void main(String[] args) {
    Integer x = new Integer(1);
    Integer y = new Integer(1);
    System.out.println(x == y);     // false   
    System.out.println(x.equals(y)); // true
}

Fakat kod aşağıdaki şekilde yazıldığında autoboxing nedeniyle her iki durumda true dönderebilir:

public static void main(String[] args) {
   
Integer x = 1;
   
Integer y = 1;
    System.
out.println(x == y);     //true
   
System.out.println(x.equals(y)); //true
}

Farklılık 3:

Nesneler karşılaştırılırken birbiriyle ilgili olmayan nesneler karşılaştırıldığında == derleyici hatası verir. equals() ise derleyici hatası vermez fakat false dönderir.

public static void main(String[] args) {
    Integer x = new Integer(1);
    Long y =
new Long(1);
    System.
out.println(x == y);   // derleyici hatası
   
System.out.println(x.equals(y)); //false
}

Yukarıdaki örnekte x ve y ikisi de 1 değerini göstermelerine rağmen  Integer ve Long tipleri birbirinden farklı tipte olduklarından x==y ifadesi derlenmeden hata verir.