Press "Enter" to skip to content

Effective Java Madde 36: Bit Alanları Yerine EnumSet Kullanın

Bir küme (set) içerisinde kullanılacağı düşünülen numalandırılmış türleri (enumerated type) temsil etmek için sıklıkla Madde 34’de gördüğümüz gruplanmış int değişmezleri yöntemi kullanılır. Ancak buradaki fark int değişkenlere yapılan atamaların bitsel işlemleri mümkün kılmak için 2’nin üsleri şeklinde olmasıdır.

// Bit alanı için numaralandırılmış tür tanımlama - KULLANMAYIN!
public class Text {
    public static final int STYLE_BOLD = 1 << 0;           // 1
    public static final int STYLE_ITALIC = 1 << 2;         // 2
    public static final int STYLE_UNDERLINE = 1 << 2;      // 4
    public static final int STYLE_STRIKETHROUGH = 1 << 3;  // 8

    // parametre olarak 0 veya daha fazla STYLE_ değişmezi 
    // bitsel veya (bitwise OR) işlemine sokulur
    public void applyStyles(int styles) { ... }
}

Bu kullanım size birden fazla STYLE_ değişmezini bitsel veya (bitwise OR) işlemine sokarak bir bit alanı elde etmenizi sağlar. Bu şekilde birden fazla değişmezin bir metoda küme biçiminde geçilmesi mümkün olmaktadır.

text.applyStyles(STYLE_BOLD | STYLE_ITALIC);

Bit alanları birleşim ve kesişim gibi küme işlemlerinin kolayca yapılabilmesini sağlar. Ancak bunlar Madde 34’de gördüğümüz int değişmezlerinin taşıdığı bütün dezavantajları fazlasıyla taşırlar. Yazdırmaya çalışırsanız veya debug ederken karşınıza çıkarsa, bir bit alanının neyi temsil ettiğini anlamak bir int değişkenine göre daha zordur. Bir bit alanının temsil ettiği elemanları taramak için de makul bir yöntem yoktur. Son olarak, API’da int mi yoksa long mu kullanmanız gerektiğini anlamak için ez fazla kaç tane bit değerine ihtiyacınız olacağını önceden doğru tahmin etmeniz gerekir. Sonradan daha fazla bit değerine ihtiyaç duyarsanız API değiştirmeden bu mümkün olmayabilir.

int değişmezleri yerine enum türleri kullanmayı tercih eden programcıların bazıları bile, değişmezler küme halinde kullanmaya müsaitse bit alanları tercih etmektedirler. Ancak bunun için bir sebep yoktur çünkü daha iyi bir seçeneğimiz var. java.util paketindeki EnumSet sınıfını kullanarak aynı enum türünden birden fazla sabiti bir arada ifade edebiliriz. Bu sınıf Set arayüzünü gerçekleştirdiği için tür güvenliği ve diğer Set türleriyle uyumlu çalışabilme avantajları sağlar. Kendi içinde ise her EnumSet bir bit vektörü olarak temsil edilir. Eğer tuttuğu sabitlerin enum türü 64 veya daha az elemandan oluşuyorsa ki çoğu zaman öyle olacaktır, tek bir long değişkeni ile kümedeki bütün elemanları temsil edebilir. Bu sebeple de bit alanlarıyla benzer bir performans ortaya koyar. Toplu küme işlemleri için kullanılan removeAll ve retainAll gibi metotlar da bit aritmetiği kullanarak hızlı bir biçimde yapılır. Bit alanları ile bunları kendiniz de yapabilirsiniz ama bitlerle uğraşmak hem çirkin kodlar yazmanıza sebebiyet verir hem de hata yapma şansı yüksektir. EnumSet bu zor ve karmaşık işleri sizin için yapar.

Az önceki örneğin bit alanları yerine bir enum türü ve EnumSet kullanılarak yazılışı aşağıdaki gibi olur. Daha kısa, daha anlaşılır ve daha güvenlidir:

// EnumSet - bit alanlarının yerini doldurmaktadır
public class Text {
    public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }

    // Herhangi bir Set geçilebilir ancak EnumSet en mantıklısıdır
    public void applyStyles(Set<Style> styles) { ... }
}

Şimdi de bir EnumSet nesnesi yaratarak applyStyles metodunu çağıran bir istemci yazalım. EnumSet sınıfı statik fabrika metotları ile kolayca nesne oluşturmamızı sağlar, bunun bir örneğini aşağıda görüyorsunuz:

text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));

Dikkat ederseniz applyStyles metodu EnumSet<Style> değil de Set<Style> kabul etmektedir. Mantıklı olan çoğu istemcinin EnumSet geçmesidir ancak gerçekleştirim türü yerine arayüz türü kabul etmek genel olarak iyi bir programlama alışkanlığıdır. (Madde 64) Böylece nadir de olsa başka türden bir Set gerçekleştirimi geçmek isteyen istemciler bunu yapabileceklerdir.

Özetle, numaralandırılmış bir tür küme (set) içinde kullanılacak diye onu bit alanlarıyla ifade etmenin bir anlamı yoktur. EnumSet sınıfı bit alanları ile elde edeceğiniz performansı size vermenin yanısıra daha güvenli ve daha anlaşılabilirdir. Bu sınıfın tek bir dezavantajı vardır o da Java 9 itibariyle değiştirilemez (immutable) bir EnumSet yaratmak mümkün değildir. Bu büyük ihtimalle sonraki Java versiyonlarında düzeltilecektir. EnumSet nesnelerini Collections.unmodifiableSet ile sarmalayabilirsiniz ama bu durumda performans kaybı olacaktır.

Share

One Comment

Leave a Reply

%d bloggers like this: