Record Sınıfı (Java 14 Yenilikleri)

Java 14 ile gelen yeniliklerden biri olan Record sınıflarını inceleyeceğiz. Bu yenilik neyi amaçlıyor;

  • Geliştiricilerin veri modellemesine daha çok odaklanmasını sağlar.
  • getter, constructor, equals, hashCode ve toString metotlarını otomatik olarak implement eder, bu şekilde standartlaşmış kod kalabalığı ortadan kalkar.

Yeni bir özellik gibi değil de POJO sınıflarının tanımını kolaylaştıran bir yapı olarak düşünülebilir.

Aslında çoğu IDE saten bu fonksiyonları otomatik oluşturuyor gibi düşünebilirsiniz fakat oluşturduktan sonra herhangi bir değişiklik yapıldığı durumda(veri tipi değişikliği, yeni bir değişken eklenmesi, vb.) düzenlemesi yapılmıyor, ek olarak sınıfta standart kod kalabalığı gözükmeye devam ediyor.

Genellikle fonksiyonlara parametre geçmek için, servislere çıktı sağlamak yada almak için, veri tabanı sorguları dönüşü için kısacası veriyi saklaması için sınıflar oluştururuz. Çoğu durumda bu verinin değiştirilmez (immutable) olması gerekmektedir.

Thread ler arasında paylaşılan objelerin değiştirilmez olmasını sağlayarak yani hiç bir thread in o objeyi değiştirmeyeceğini garantilersek thread safe hale getirebiliriz. Bu yapı Immutable olarak adlandırılır.

Record sınıf objeleri de bu kriteri sağlamaktadır. Obje ilk oluştuktan sonra değiştirilemez olur. Bunu aslında private final değişkenler ve setter fonksiyonu olmadan sağlamış oluyor.

Aşağıdaki gibi record component tanımladığımızda otomatik olarak gerçekleşen işlemlere göz atalım;

public record Product(String name, double price) {}

→ Header kısmında bulunan her değişken için aynı isim ve tipte private final değişkenler oluşturulur. Bunlar component field olarak da adlandırılır.

private final String name;
private final double price;

→ Header kısmında bulunan her değişken için aynı isimde getter fonksiyonları oluşturulur. Fonksiyonlar isimleri değişken adı ile aynıdır.

public String name() { return this.name; }
public double price() { return this.price; }

→ Header da yer alan bütün değişkenleri içeren constructor oluşturulur.

public Product(String name, double price) {
this.name = name;
this.price = price;
}

→ Component üzerinde ki bütün değişkenlerin tipinin ve değerinin aynı olup olmadığını kontrol eden equals ve hashCode fonksiyonlarını oluşturur.

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProductC productC = (ProductC) o;
return Double.compare(productC.price, price) == 0 && Objects.equals(name, productC.name);
}

@Override
public int hashCode() {
return Objects.hash(name, price);
}

→ Component üzerindeki bütün değişkenleri isimleri ile birlikte String değerlerini içeren toString fonksiyonunu oluşturur.

@Override
public String toString() {
return "Product[" +
"name='" + name + ','' +
", price=" + price +
']';
}

Record sınıfları özelleştirilmiş bir çeşit sınıf gibi düşünebilir. Sınıf objesi oluşturur gibi new anahtar kelimesi kullanılarak record objesi oluşturulabilir. Aşağıdaki örnekte olduğu gibi bir Product objesi oluşturduğumuzda ekstra toString fonksiyonu oluşturmamıza gerek kalmadan objeyi String olarak görüntüleyebiliyoruz. Yine aynı şekilde getter fonksiyonlarını çağırabiliyoruz.

Product product = new Product("Book", 2.3);
System.out.println(product);
System.out.println(product.name());
System.out.println(product.price());
Product[name=Book, price=2.3]
Book
2.3

Az çok yazılım dünyasında yer almış biri her şeyin standartta olduğu gibi kalmayacağını, buna izin verilmeyeceğini bilir :) Bu yüzden Record sınıflarında oluşan fonksiyonları override etme imkanımız da bulunuyor. Fakat bu çok tavsiye edilmiyor yada yapılacak değişikliğin yapısal değil de ek kontrol yada validasyonlar için olabileceği şeklinde yönlendirme bulunuyor.

Kendi örneğimizden yola çıkarsak Product objesinde price değerinin 0 yada daha küçük bir değer olmasını engellemek istiyoruz. Bu durumda oluşan parametreli constructor yeniden tanımlanabilir yada aşağıdaki gibi default constructor eklenerek bunu sağlayabiliriz.

public record Product(String name, double price) {
public Product {
if (price <= 0) {
throw new java.lang.IllegalArgumentException(
String.format("Invalid Price: %f, ", price));
}
}
}

Bu fonksiyonları implement ederken olması gereken özellikler ile aynı şekilde yazılması gerekmektedir. Örnek olarak price metodunu güncelliyoruz aynı return tipini döndürmesi ve aynı metod isminde olması gerekmektedir.

Record içerisinde kendi metotlarımızı oluşturabiliyoruz, statik değişkenler ve fonksiyonlar da tanımlayabiliyoruz. Fakat aşağıdaki koşullara dikkat etmemiz gerekmektedir:

🚩Record sınıf final olarak tanımlanır ve abstract olarak oluşturulamaz.

🚩 Record tanımı sırasında extend anahtar kelimesi kullanılamaz. Üst sınıfı Record sınıfı olan bir sınıftan bile türetilemez. (final olarak tanımlanmış bir sınıf extend edilemez )

🚩Record tanımı içeresinde yer alan değişkenler de final olarak tanımlanır. Sebebine yukarıda değinmiştik.(veri taşıyan sınıflar için immutable olmasını sağlamak)

🚩Record içinde sınıf değişkeni tanımı yapılamaz. Bu kısıtlama verinin sadece parametre olarak değişkende tutulmasını sağlar.

--

--