Bir yılın içindeki mevsimler, haftanın günleri, güneş sistemindeki gezegenler gibi önceden belli bir takım değerler taşıyan türlere numaralandırılmış tür (enumerated type) denir. Enum türleri Java’ya eklenmeden önce numaralandırılmış türleri ifade edebilmek için aşağıdaki gibi gruplanmış int
değişmezleri (constant) kullanılıyordu:
// int değişmezleri kullanımı - ciddi olarak kusurlu!
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
Yukarıdaki gibi int
değişmezlerinin gruplandığı bir kullanımın çok sayıda eksikliği vardır. Tür güvenliği sağlama noktasında hiçbir katkısı yoktur. Elma bekleyen bir metoda portakal geçtiğiniz zaman derleyici şikayet etmeyecektir ve ==
işlecini kullanarak elmayla portakalı karşılaştırabilirsiniz. Hatta bunları matematiksel işlemlere sokabilirsiniz:
// turunçgil aromalı elma püresi
int i = (APPLE_FUJI - ORANGE_TEMPLE) / APPLE_PIPPIN;
Dikkat ederseniz değişmezleri gruplarken elma grubu için APPLE_
portakal grubu içinse ORANGE_
öneklerini (prefix) kullandık. Bunun amacı farklı değişmez grupları için isim çakışmalarını engellemektir: ELEMENT_MERCURY
ve PLANET_MERCURY
gibi.
Bu şekilde int
değişmezlerini kullanan programlar çok kırılgan olur. Bu değişmezlerin int
değerleri derleme anında istemcilere gömülür. Eğer daha sonradan bu değerlerde bir değişiklik olursa istemcilerin yeniden derlenmesi gerekir. Derlenmezlerse istemciler yine de çalışır ama davranışları hatalı olur.
Bu değişmezleri yazdırabilecek bir biçime sokmak da kolay değildir. Yazdırmayı denediğinizde veya debug ederken göreceğiniz tek şey bir int
değeri olacaktır ve bu da pek faydalı değildir. Bir grupta yer alan değişmezlerin toplam sayısını bulmak veya bunları taramak (iterate) için güvenli bir yöntem yoktur.
Bunun bir alternatifi olarak, int
değişmezleri yerine String
kullanılan örnekler görebilirsiniz ancak bu da benzer problemlere yol açacağından dolayı önerilmez.
Java dili bizlere bütün bu problemlerden arındırılmış bir seçenek olarak enum türlerini sunmaktadır. Enum türleri en basit haliyle aşağıdaki gibi yazılabilir. Burada Apple
ve Orange
türlerini temsil etmek için iki farklı enum tanımlanmıştır. Bunların içindeki FUJI, NAVEL
, TEMPLE
gibi alanlar enum sabiti olarak adlandırılır ve bu sabitlere Apple.FUJI, Orange.NAVEL, Orange.BLOOD
şeklinde erişilir.
public enum Apple { FUJI, PIPPIN, GRANNY_SMITH }
public enum Orange { NAVEL, TEMPLE, BLOOD }
Görüntü olarak enumlar C, C++ gibi dillerdeki karşılıklarına benzeseler de çok daha güçlü ve esnektirler.
Enumlar mantık olarak aslında çok basittir. Tanımlı her enum sabitini public static final
bir alan olarak dışa açan sınıflar olarak düşünülebilirler. Enum türleri kalıtılamazlar ve dışarıdan erişilebilir yapıcı metotları olmadığından başka kod parçaları tarafından yaratılamazlar. Enum türü içerisinde tanımlı sabitler haricinde nesnelerinin olamayacağı garanti edilir. Enumlar için Madde 3’de anlatılan, özünde tek elemanlı enum olan singletonların genelleştirilmiş halidir de diyebiliriz.
Enumlar derleme anında tür güvenliği sağlarlar. Eğer bir metot Apple
türünde bir parametre bekliyorsa, siz bu metoda Apple
tanımında belirlenmiş 3 enum sabitinden birini veya null
geçebilirsiniz. Orange
gibi başka bir türden nesne referansı geçmeye çalışırsanız derleyici izin vermeyecektir. Bir enum türünü başka bir enum türüne atamaya çalışırsanız veya ==
işleci ile eşitlik kontrolü yapmaya çalışırsanız da yine derleyici izin vermez.
Farklı enum türleri içinde aynı isimli enum sabitleri tanımlayabilirsiniz çünkü her enum türü kendi isim uzayına (namespace) sahiptir. İstemcilere dokunmadan yeni enum sabitleri ekleyebilir veya bunların yerlerini değiştirebilirsiniz. Son olarak enum türlerinin toString
metodunu çağırarak yazdırılabilir bir String
elde edebilirsiniz.
Enum türleri özünde sınıf oldukları için içlerine alan (field) ve metotlar ekleyebilir hatta arayüzler uygulayabiliriz. Enumlar Object
sınıfından gelen bütün metotları kaliteli bir biçimde geçersiz kılarlar ve Comparable
ile Serializable
arayüzlerini uygularlar. Bütün bunlar biz fazladan tek satır yazmadan derleyici tarafından yapılmaktadır.
Peki bir enum türüne neden metot eklemek isteyebiliriz? Bu genelde enum sabitiyle ona ait başka verileri ilişkilendirmek için yapılır. Bizim Apple
ve Orange
türlerini düşünürsek, örneğin enum sabitinin temsil ettiği meyvenin rengini veya görüntüsünü döndüren bir metot ekleyebiliriz. Bir enum türü peş peşe yazılmış enum sabitleri olarak hayatına başlayıp, sonradan yeni metotlar ve alanlar eklenerek geliştirilebilir.
Biraz daha gelişmiş bir enum türüne örnek olsun diye güneş sistemimizdeki 8 gezegeni ele alalım. Her gezegenin bir kütlesi ve çapı vardır, bunlardan faydalanarak her gezegen için yerçekimini hesaplayabiliriz. Bu da bize dünyadaki ağırlığını bildiğimiz bir cismin farklı gezegenlerdeki ağırlığını hesaplama imkanı verir. Şimdi bunlar bir enum türüyle nasıl ifade edilir ona bakalım. Her enum sabitinden sonra parantez içinde yazılı değerler enum türünün yapıcı metoduna geçilmektedir. Burada bu değerler gezegenin kütlesi ve çapıdır:
// ek veri ve davranış içeren enum türü
public enum Planet {
MERCURY(3.302e+23, 2.439e6),
VENUS (4.869e+24, 6.052e6),
EARTH (5.975e+24, 6.378e6),
MARS (6.419e+23, 3.393e6),
JUPITER(1.899e+27, 7.149e7),
SATURN (5.685e+26, 6.027e7),
URANUS (8.683e+25, 2.556e7),
NEPTUNE(1.024e+26, 2.477e7);
private final double mass; // kilogram
private final double radius; // metre
private final double surfaceGravity; // m / s^2
// evrensel yerçekimi sabiti in m^3 / kg s^2
private static final double G = 6.67300E-11;
// yapıcı metot
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
surfaceGravity = G * mass / (radius * radius);
}
public double mass() { return mass; }
public double radius() { return radius; }
public double surfaceGravity() { return surfaceGravity; }
public double surfaceWeight(double mass) {
return mass * surfaceGravity; // F = ma
}
}
Bu şekilde ek alanlar ve metotlarla zenginleştirilmiş enumlar yazmak çok zor değildir. Enum sabitlerini başka verilerle ilişkilendirmek için nesne alanları (instance field) tanımlayabilir ve yapıcı metot yardımıyla bunlara atama yapabilirsiniz. Enumlar doğası gereği değişmez (immutable) oldukları için bütün alanların final
olması tavsiye edilir. (Madde 17) Alanları public
tanımlamaya izin vardır ama private
yapıp erişim metodu ile dışa açmak daha doğru olur. (Madde 16) Planet
enumunda, yapıcı metot yerçekimini de hesaplayıp saklamaktadır ancak bu bir optimizasyondur. Bunun yerine surfaceWeight
metodu çağrıldığında hesaplamak da mümkündür.
Planet
enum türü basit olmasına karşın çok güçlüdür. Aşağıda, cisimlerin dünyadaki ağırlığını alıp bütün gezegenlerdeki ağırlığını hesaplayarak yazdıran bir istemci program görüyoruz:
public class WeightTable {
public static void main(String[] args) {
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight / Planet.EARTH.surfaceGravity();
for (Planet p : Planet.values()) {
System.out.printf("Weight on %s is %f%n",
p, p.surfaceWeight(mass));
}
}
}
Şimdi bu program üzerinde biraz konuşalım. Planet
bütün enumlar gibi statik values
metoduna sahiptir. Bu metot enum içindeki tanımlı bütün sabitleri tanımlandıkları sırada bir dizi içerisinde döndürür ve böylece for
döngüsünde bütün sabitleri tek tek tarayabiliriz. Enum türleri için toString
metodu enum sabitinin adını döndürecek şekilde otomatik olarak eklenir ancak bunu değiştirmek için toString
metodunu geçersiz kılabilirsiniz. Enumlar toString
metoduna sahip oldukları için println
veya printf
gibi metotlarla kullanılabilirler. Yukarıda yazdığımız WeightTable
programının çıktısı aşağıdaki gibi olacaktır:
Weight on MERCURY is 69.912739 Weight on VENUS is 167.434436 Weight on EARTH is 185.000000 Weight on MARS is 70.226739 Weight on JUPITER is 467.990696 Weight on SATURN is 197.120111 Weight on URANUS is 167.398264 Weight on NEPTUNE is 210.208751
Bildiğiniz gibi bir zamanlar Pluto da gezegen zannediliyordu. Peki biz bu enum türünü o zamanlarda yazmış olsak ve Pluto için de Planet.PLUTO
diye bir enum sabiti tanımlamış olsaydık, sonradan bunu silmemiz problem yaratır mıydı? Silinen enum sabitine direk olarak erişmeyen programlarda bir sorun çıkmazdı. Örneğin bizim WeightTable
programımızda var olan enum türlerini taradığımız için Planet.PLUTO
sabitini silseydik program 9 yerine 8 gezegen için çıktı üretecekti. Ancak silinen enum sabitine direk erişen programların tekrar derlenmesi gerekir. Derleme sonucunda hata mesajı açık bir biçimde artık var olmayan enum sabitini işaret eder. İstemci derlenmezse bu sefer çalışma zamanında benzer bir hata alınır. Bu da zaten bekleyebileceğimiz en makul sonuçtur.
Enum sabitleriyle ilişkilendirmek istediğiniz davranışların bir kısmına dışarıdan erişim gerekmeyebilir. Böyle metotların private
veya package-private
yazılması en doğrusu olur. Aynen sınıflarda olduğu gibi bir enum metodunu dışarı açmak için mantıklı bir sebep yoksa private
veya bunu yapamıyorsanız package-private
tanımlayın. (Madde 15)
Eğer bir enum türü birçok yerde faydalı olacaksa kendi başına tanımlanmalıdır. Ama kullanımı tek bir sınıfla sınırlı kalıyorsa üye sınıf (member class) olabilir. (Madde 24) Örneğin, java.math.RoundingMode
enum türü küsuratlı sayılar için yuvarlamanın nasıl yapılacağını temsil eder. Bu yuvarlama modları BigDecimal
sınıfı tarafından kullanılır ancak başka yerlerde de faydalı olabileceği için RoundingMode.java
dosyasında üst seviye bir tür olarak tanımlanmıştır. Bu şekilde RoundingMode
enum türünün yeniden kullanılabilir olması sağlanmıştır.
Planet
örneğinde gördüğümüz kullanımlar birçok enum türünü tanımlayabilmek için fazlasıyla yeterlidir ancak bazen daha fazlası da gerekebilir. Her bir Planet
enum sabiti için farklı mass
ve radius
gibi ilişkilendirilmiş alanlar vardır ancak bazen de bu enum sabitlerine farklı davranışlar yüklemek isteyebilirsiniz. Farzedelim ki dört işlem yapabilen bir hesap makinesinin işlevlerini tanımlamak için bir enum türü oluşturmak istiyoruz ve enum sabitlerinin bu işlevleri nasıl yapması gerektiğini bir şekilde kodlamak istiyoruz. Bunu yapmanın bir yolu aşağıdaki gibi switch
deyimi kullanmaktır:
// kendisi (this) üzerinde switch uygulayan enum türü
public enum Operation {
PLUS, MINUS, TIMES, DIVIDE;
// bu enum sabitinin temsil ettiği matematiksel işlemi uygula
public double apply(double x, double y) {
switch(this) {
case PLUS: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
}
throw new AssertionError("Unknown op: " + this);
}
}
Bu kod çalışır ama açıkçası pek de güzel durmuyor. Her ne kadar biz bir aykırı durumla karşılaşmayacağımızı bilsek de, AssertionError
fırlattığımız 12. satırı yazmazsak derleme hatası alırız çünkü bu satır teknik olarak erişilebilirdir. İşin kötüsü, yeni bir enum sabiti eklediğimizde bunu switch
bloğuna eklemeyi unutursak yeni işlemi uygulamaya çalıştığımızda kod çalışma zamanında çökecektir.
Neyse ki farklı enum sabitleri için farklı davranışlar tanımlayabilmemizi sağlayan daha iyi bir yol var. Enum türü içinde soyut (abstract) bir apply
metodu tanımlayıp her bir sabit için bunu geçersiz kılabiliriz.
//Enum sabitlerine özel metot gerçekleştirimleri
public enum Operation {
PLUS {
public double apply(double x, double y) {
return x+y;
}
},
MINUS {
public double apply(double x, double y) {
return x - y;
}
},
TIMES {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE {
public double apply(double x, double y) {
return x / y;
}
};
public abstract double apply(double x, double y);
}
Burada gördüğünüz gibi her enum sabiti için bir gövde içerisinde soyut apply
metot olması gerektiği gibi gerçekleştirilmiştir. Yeni bir enum sabiti ekleme durumunda bu sabitin davranışını da tanımlamak zorundasınız çünkü her enum sabiti apply
metodunu geçersiz kılmak zorundadır. Aksi taktirde kod derlenmeyecektir.
Bu şekilde enum sabitlerine davranış eklerken aynı zamanda veri de tutabiliriz. Örneğin, aşağıdaki gibi her enum sabitine bir sembol atayıp toString
metodunu geçersiz kılarak bu sembolleri döndürebiliriz.
// Her enum sabiti için farklı davranış ve alan kullanan enum türü
public enum Operation {
PLUS("+") {
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
public double apply(double x, double y) {
return x - y;
}
},
TIMES("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;
Operation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
public abstract double apply(double x, double y);
}
Bu toString
gerçekleştirimi sayesinde aritmetik işlemleri aşağıdaki gibi basit bir programla yazdırabiliriz:
public static void main(String[] args) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
for (Operation op : Operation.values())
System.out.printf("%f %s %f = %f%n",
x, op, y, op.apply(x, y));
}
Bu programı 2
ve 4
argümanlarını komut satırından geçerek çalıştırırsak aşağıdaki gibi bir sonuç üretir:
2.000000 + 4.000000 = 6.000000 2.000000 - 4.000000 = -2.000000 2.000000 * 4.000000 = 8.000000 2.000000 / 4.000000 = 0.500000
Enum türleri otomatik üretilen bir valueOf(String)
metoduna sahiptir. Bu metot enum sabitinin adını geçtiğiniz zaman size sabitin kendisini döndürür. Eğer toString
metodunu geçersiz kılarsanız bunun yanında bir de fromString
yazmayı düşünün ki istemciler enum sabitinin kendisi ile toString
ile belirlenmiş String
formu arasında geçiş yapabilsinler. Operation
türü için toString
metodu enum sabitini temsil eden sembolü döndürecek şekilde yazıldığı için, fromString
metodunun da verilen bir sembol değerine karşılık gelen enum sabitini döndürmesi beklenir. Aşağıdaki kod bu amaçla Operation
enum türü için yazılmıştır ama başka enumlar için de kullanılabilir (her enum sabitinin kendine özgü – unique – bir String
karşılığı olduğu sürece):
// Enum türleri için fromString gerçekleştirimi
private static final Map<String, Operation> stringToEnum =
Stream.of(values()).collect(toMap(Object::toString, e -> e));
// symbol değerine göre varsa Operation döndürür
public static Optional<Operation> fromString(String symbol) {
return Optional.ofNullable(stringToEnum.get(symbol));
}
Burada dikkat edilecek birkaç husus var. Öncelikle Operation
enum sabitleri statik bir Map
olan stringToEnum
içerisine bu alan tanımlanırken ekleniyor. Bu ilklendirme (initialization) işlemi enum sabitleri yaratıldıktan sonra yapılır, dolayısıyla values()
çağrısı bize bütün enum sabitlerini içeren bir dizi döndürecektir. Bu kod daha sonra dizi üzerinden bir stream
yaratmakta ve enum sabitlerini temsil edildikleri sembollere denk gelecek şekilde stringToEnum
içine Map
girdileri (entry) olarak eklemektedir. Streamler kitabın 7. bölümünde ayrıca ele alınmaktadır.
Java 8’den önce bunu yapabilmek için önce boş bir HashMap
yaratıp daha sonra values
dizisinin üzerinde tarama yaparak bütün elemanları tek tek eklemeniz gerekirdi. Aklınıza yapıcı metot işletilirken her enum sabitini stringToEnum
içerisine kaydetmek gibi bir çözüm gelebilir ancak bu çalışmayacaktır çünkü enum sabitlerinin yapıcı metotlarından statik alanlara erişimi bazı istisnalar haricinde yasaktır. Bunun sebebi daha önce de bahsedildiği gibi enum sabitlerinin statik alanlardan daha önce yaratılmasıdır.
fromString
metodunun Optional<String>
döndürdüğünü de gözden kaçırmayın. Bu sayede enum karşılığı olmayan bir sembol fromString
metoduna verildiğinde dönüş türü bunu ifade edebilecektir ve istemci de bu durumu ele almak için zorlanmış olacaktır. (Madde 55)
Operation
gerçekleştiriminde uyguladığımız yöntemin bir dezavantajı enum sabitleri arasında kod paylaşımı yapmayı zorlaştırmasıdır. Örneğin, bir maaş ödeme sistemi içerisinde haftanın günlerini temsil eden bir enum türümüz olsun. Bu enum her gün için çalışana ödenmesi gereken maaş miktarını hesaplarken o gün toplamda kaç dakika çalıştığına bakmaktadır. Hafta içi günlerde mesai saatleri haricinde çalışılan dakikalar için ek mesai ücreti ödenmekte, haftasonları içinse çalışılan her dakika için ek mesai ödenmektedir. switch
deyimi kullanırsak bunu aşağıdaki gibi kodlamak aslında kolaydır:
// switch deyimi ile kod paylaşımı sağlayan enum - tartışmaya açık
enum PayrollDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
SATURDAY, SUNDAY;
private static final int MINS_PER_SHIFT = 8 * 60;
int pay(int minutesWorked, int payRate) {
int basePay = minutesWorked * payRate;
int overtimePay;
switch(this) {
case SATURDAY:
case SUNDAY: // haftasonu
overtimePay = basePay / 2;
break;
default: // haftaiçi
overtimePay = minutesWorked <= MINS_PER_SHIFT ?
0 : (minutesWorked - MINS_PER_SHIFT) * payRate / 2;
}
return basePay + overtimePay;
}
}
Bu kod kısa olmasına rağmen bakımı zor olduğundan ötürü tehlikelidir. Enum türüne tatil günlerini temsil etmek için yeni bir değer eklediğimizi düşünelim. switch
bloğuna bu yeni değer için gerekli kodu eklemeyi unutursak kod yine de çalışacaktır ama tatil günlerinde hesaplanan ödeme miktarı hafta içi ile aynı olacaktır.
Eğer switch
deyimi kullanmayıp Operation
türünde yaptığımız gibi her enum sabiti için metot tanımlarsak da bu sefer aynı kodu farklı günler için kopyalamamız gerekir. Bundan kurtulmak için hafta içi ve hafta sonu günlerin ödemesini hesaplayan iki tane yardımcı metot yazıp enum sabitindeki geçersiz kılınmış metotlardan bunları çağırabiliriz ancak bu durumda bile bir sürü gereksiz kod yazmak zorunda kalırız. Bu da hem kodun okunabilirliğini azaltır hem de hata yapmayı kolaylaştırır.
Kodumuzu her enum sabiti için bir ek mesai ödeme stratejisi seçmek zorunda olacak şekilde tasarlarsak buradaki problemlerden kurtulabiliriz. Bunu yapmak için kullanabileceğimiz bir yöntem var. Ek mesai ücreti hesaplaması yapan kısımlar hafta içi ve hafta sonu günler için farklılık gösterdiğinden dolayı bunları bir gömülü enum (private nested enum) içerisine taşıyabiliriz. PayrollDay
sabitlerini yaratırken de bu gömülü enumda tanımlı ek mesai hesaplama stratejilerinden birini seçebiliriz. Bunu daha iyi anlamak için koda bakalım. Her ne kadar daha karmaşık ve uzun görünse de aslında daha güvenli ve esnektir:
// Strateji enum deseni
enum PayrollDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
SATURDAY(PayType.WEEKEND), SUNDAY(PayType.WEEKEND);
private final PayType payType;
PayrollDay(PayType payType) {
this.payType = payType;
}
PayrollDay() {
this(PayType.WEEKDAY); // varsayılan/default
}
int pay(int minutesWorked, int payRate) {
return payType.pay(minutesWorked, payRate);
}
// strateji enum türü
private enum PayType {
WEEKDAY {
int overtimePay(int minsWorked, int payRate) {
return minsWorked <= MINS_PER_SHIFT ? 0 :
(minsWorked - MINS_PER_SHIFT) * payRate / 2;
}
},
WEEKEND {
int overtimePay(int minsWorked, int payRate) {
return minsWorked * payRate / 2;
}
};
abstract int overtimePay(int mins, int payRate);
private static final int MINS_PER_SHIFT = 8 * 60;
int pay(int minsWorked, int payRate) {
int basePay = minsWorked * payRate;
return basePay + overtimePay(minsWorked, payRate);
}
}
}
Burada farklı günler için değişkenlik gösteren iki farklı ek mesai (overtime) hesaplama algoritması PayType
gömülü enumu içine kodlanmıştır. PayrollDay
enum sabitleri yaratılırken de PayType.WEEKDAY
veya PayType.WEEKEND
stratejilerinden birini ek mesai hesaplama stratejisi olarak seçmektedirler. Bu kullanım mantık olarak Strateji Tasarım Desenine çok benzemektedir.
Peki switch
deyimlerini enum sabitlerine davranış eklemek için kullanmak uygun değilse ne amaçla kullanabiliriz? Farzedelim ki Operation
türü sizin kontrolünüzde bir enum değil ve siz keşke tanımlı her işlevin tersini döndüren bir metot daha olsaydı diye düşünüyorsunuz. (toplama için çıkarma döndürsün, çarpma için bölme döndürsün gibi) Bunu simüle etmek için switch
deyimini aşağıdaki gibi kullanabilirsiniz:
// Enumda eksik olan metodu simule etmek için switch kullanımı
public static Operation inverse(Operation op) {
switch(op) {
case PLUS: return Operation.MINUS;
case MINUS: return Operation.PLUS;
case TIMES: return Operation.DIVIDE;
case DIVIDE: return Operation.TIMES;
default: thrownewAssertionError("Unknown op:"+op); }
}
Enum türleri genel olarak int
değişmezleri ile benzer performans sergilerler. Enum sabitlerinin yaratılması sırasında ufak bir zaman ve bellek kaybı olsa da pratikte bu fark anlaşılmaz.
Peki enumları ne zaman kullanmalıyız? Derleme anında değerlerini bildiğiniz bir grup değişmeze ne zaman ihtiyaç duyarsanız enum kullanın. Bunlar gezegenler, günler, satranç taşları gibi doğadan örnekler olabileceği gibi sizin uygulamanıza özgü olarak derleme anında değerlerini bildiğiniz bir menüdeki seçenekler, işlem kodları, komut satırı bayrakları (command line flags) gibi öğeleri de temsil edebilirler. Bir enum türünde tanımlı sabitlerin her zaman aynı kalması gibi bir şart yoktur. Enum türleri ekleme ve çıkarmalar yapılırken kullanıcılarını minimum düzeyde etkileyecek şekilde tasarlanmıştır.
Özetle, int
değişmezlerine kıyasla enum türlerinin avantajları çok fazladır. Enumlar daha güvenli, okunabilir ve güçlüdür. Birçok enum için ek alanlar veya metotlar tanımlamanıza gerek kalmaz ama tanımladığınız sabitlerle ilişkilendirmek istediğiniz veriler veya davranışlar varsa bunu yapabilirsiniz. Her enum sabiti için farklı davranışlar belirlemeniz gereken durumlarda switch
deyimi ile farklı davranışları kodlamak yerine enum içinde soyut bir metot tanımlayıp her enum sabiti için bu metodu geçersiz kılın. Eğer bazı enum sabitlerinin davranışları birbiriyle aynı ise strateji enum desenini uygulayın.
[…] 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 […]
[…] Madde 34’de enumlardan bahsederken enum türlerinin kalıtılamayan (final) sınıflara dönüştürüldüğü söylemiştik. Dolayısıyla bir enum türü başka bir enum türünü kalıtamaz. Enum türleri zaten dolaylı olarak java.lang.Enum sınıfından türetildikleri için başka sınıfları kalıtması da mümkün değildir. […]
[…] stream elemanlarını bir anahtarla, ikinci fonksiyon ise bir değerle eşlemek için kullanılır. Madde 34’de fromString metodunu yazarken bunu kullanmıştık. Burada anahtar olarak enum sabitinin String […]