Press "Enter" to skip to content

Effective Java Madde 62: Diğer Türlerin Daha Elverişli Olduğu Durumlarda String Kullanmaktan Kaçının

String türü, metinleri temsil etmek için tasarlanmıştır ve bu işi gayet iyi yapmaktadır. Ancak stringler çok yaygın ve kullanımı kolay olduğundan, uygun olmadıkları durumlar için de kullanıldığını görmekteyiz. Bu maddede, stringleri kullanmamanız gereken durumlardan bahsedeceğiz.

Stringler diğer değer türlerinin (value types) yerine kullanılmamalıdır. Bir programa veri akışı olduğunda (örneğin bir dosya, ağ veya kullanıcının kendisinden) bu veri genellikle String biçiminde olur. Doğal bir eğilim olarak programcılar veriyi bu şekilde bırakabilirler ancak bu sadece veri bir metni temsil ediyorsa mantıklı olacaktır. Veri eğer sayısal bir değerse int, float veya BigInteger gibi bir türe dönüştürülmelidir. Eğer bu veri bir evet-hayır sorusunun cevabı ise, bu amaç için tasarlanmış boolean türüne dönüştürülmelidir. Daha genel olarak söylemek gerekirse, veriyi saklamak için daha elverişli bir değer türü varsa o kullanılmalıdır. Bu çok açık görünse de sıklıkla ihmal edilmektedir.

Stringler enum türlerinin yerine kullanılmamalıdır. Madde 34’de anlatıldığı gibi, enum türleri kullanarak numaralandırılmış tür sabitlerini tanımlamak çok daha doğru olacaktır.

Stringler birden fazla bileşenden oluşan türleri temsil etmek için kullanılmamalıdır. Örneğin,

// Uygunsuz string kullanımı
String compoundKey = className + "#" + i.next();

Bu kullanımın çok sayıda dezavantajı vardır. İki değeri birbirinden ayırmak için kullanılan “#” karakteri bu değerlerden birinde bulunursa problem çıkartır. Birleştirdikten sonra bu iki değeri tekrar elde etmek istersek stringi parçalamamız gerekir ve başka hatalara yol açabilir. Ayrıca kendi equals, toString veya compareTo metotlarınızı yazamazsınız çünkü String sınıfından gelenleri kullanmaya mecbur kalırsınız. Buna daha iyi bir alternatif olarak, Madde 24’te anlatıldığı gibi private statik bir üye sınıf yazıp bunu kullanmak daha doğru olacaktır.

Not: Yazının geri kalanını daha kolay anlayabilmek için ThreadLocal kavramına aşina olmanız gerekmektedir. Bu, Java’da kabaca threadlere özgü değişkenler tanımlamak için kullanılan bir yöntemdir. Daha fazla bilgi almak için bu yazıya bakabilirsiniz.

String değişkenlerini bir takım işlevleri temsil etmek için kullanmak da sakıncalıdır. Örneğin, thread-local değişkenleri saklamamızı sağlayan bir sınıf yazmak istediğimizi düşünelim. Java 1.2’den itibaren bu özellik Java’da zaten bulunmaktadır ama bunun öncesinde Java programcıları bunu kendileri yazmak zorunda kalmışlardır. Bu problemle karşılaşan programcıların bir çoğu, birbirlerinden bağımsız olarak aynı tasarımla ortaya çıkmışlardır. Bu tasarımda her bir thread-local değişkeni tanımlamak için String türünde anahtarlar kullanılmıştır:

// Bozuk - Stringleri yetenek ifade etmek için kullanıyor!
public class ThreadLocal {
    private ThreadLocal() { } // nesne oluşturulamaz
    // Aktif thread için değişken ataması yapar 
    public static void set(String key, Object value);
    // Aktif threadin bu değişken için sahip olduğu değeri döndürür
    public static Object get(String key);
}

Burada String türündeki anahtar değerler global isim uzayındaki thread-local değişkenleri temsil etmektedir. Bu tasarımın çalışabilmesi için, istemci tarafından belirlenen anahtar değerlerinin (key) eşsiz (unique) olması gerekmektedir. Eğer iki farklı istemci birbirinden habersiz aynı ismi kullanırsa aynı değişkeni kullanmış olurlar ve bu da ciddi sorunlara yol açar. Bu aynı zamanda güvenlik açığı da doğurur çünkü kötü niyetli bir istemci aynı anahtar değerini kullanarak diğer istemcilerin verilerine ulaşabilir.

Buradaki problemi gidermek için String yerine aşağıdaki gibi bir üye sınıf yaratıp onu key olarak kullanabiliriz.

public class ThreadLocal {
    private ThreadLocal() { } // nesnesi yaratılamaz
    public static class Key { 
        Key() { }
    }
    // Güvenli ve eşsiz bir key üretir
    public static Key getKey() {
        return new Key();
    }
    public static void set(Key key, Object value);
    public static Object get(Key key);
}

Yukarıdaki kod String tabanlı tasarımın sorunlarını çözse de bunun daha iyisini yapabiliriz. Benzer bir işlevselliği aşağıdaki şekilde daha basit bir tasarımla da yakalayabiliriz:

public final class ThreadLocal {
    public ThreadLocal();
    public void set(Object value);
    public Object get();
}

Bu daha basit ama yine de bir sorun var. Object türünde veri sakladığı ve geri döndürdüğü için tür güvenliği yoktur. İstemcilerin tür dönüşümünü kendisi yapması gerekmektedir. Başka verdiğimiz String tabanlı tasarımı tür güvenlikli hale getirmek imkansızdır, Key sınıfı kullandığımız tasarımda da bu zor olacaktır. Ancak bu basit tasarımı tür güvenlikli hale getirmek için tek yapmamız gereken sınıfa tür parametresi eklemektir (Madde 29):

public final class ThreadLocal<T> {
    public ThreadLocal();
    public void set(T value);
    public T get();
}

Bu API, kabaca şu anda java.lang.ThreadLocal ile sunulmaktadır. Hem String örneğinde gördüğümüz problemleri çözmektedir hem de Key sınıfı kullandığımız tasarıma kıyasla daha basit ve daha hızlı çalışmaktadır.

Özetle, daha farklı türlerle ifade edebildiğimiz verileri String ile temsil etmekten kaçınmalıyız. Yanlış kullanıldığında stringler esnekliği azaltır, daha yavaştır ve hata yapmamıza sebebiyet verebilirler. Stringler genellikle temel türler, enumlar ve bileşik türlerin yerine yanlış olarak kullanılırlar.

Share

Leave a Reply

%d bloggers like this: