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.
[…] EnumSet metodunun statik fabrikaları, nesne yaratma maliyetini minimuma indirmek için bu tekniği kullanır. Burada kullanılması mantıklı olmuştur çünkü EnumSet bit alanlarına performanstan ödün vermeden bir alternatif olabilmesi için tasarlanmıştır. (Madde 36) […]