15 Mayıs 2020 Cuma

JEP 359 - record Anahtar Kelimesi - Java 16 İle Geliyor

Giriş
Açıklaması şöyle. Tüm alanları private olan ve setter'a sahip olmayan immutable bir nesne döner. Bu nesne için toString(), equals() ve hashCode() üretilir. Ayrıca bu nesne java.lang.Object.Record sınıfından kalıtır. Dolayısıyla başka bir sınıftan kalıtmak mümkün değil.
Essentially, a record will be a class that intends to have only final fields that are set in the constructor.
Ayrıca record sınıfları bean değildir. Açıklaması şöyle
Bean Convention
Records are not meant to be compliant with bean conventions. The accessor methods are not named with getX and the class does not provide setters nor a no-args constructor.
Not
record ve Comparable yazısına bakabilirsiniz
record ve Serialization yazısına bakabilirsiniz
 

1. Lombok İle Kıyaslama
record varsa Lombok kullanmaya gerek yok deniliyor. Açıklaması şöyle
It will also help teams eliminate many hand-coded implementations of the underlying pattern and reduce or remove the need for libraries like Lombok.
Örnek
Elimizde şöyle bir kod olsun
record Person (String name, String surname) {}
Aynı kodu Lombok ile şöyle yaparız.
@AllArgsConstructor
@Data
public class GetterSetterExample {
  private int name;
  private int surname;
}
record varsa Lombok'a gerek yok ifadesi her zaman tam doğru olmayabilir. Özellikle kalabalık sınıflarda bence Lombok Builder daha iyi.
Örnek
Elimizde şöyle bir record olsun
public record DetailedCar( String brand, String model, int year, String engineCode, String engineType, String requiredFuel, String fuelSystem, String maxHorsePower, String maxTorque, float fuelCapacity) { }
Bunu kullanmak için şöyle yapmak gerekir
DetailedCar camaroDetailed = new DetailedCar( "Chevrolet", "Camaro", 2022, "LTG", "Turbocharged", "Gas I4", "Direct Injection", "275 @ 560", "295 @ 3000-4500", 19.0f);
Lombok ile daha okunaklı olabilir. Şöyle yaparız
@Builder public class DetailedCar { @NonNull private String brand; @NonNull private String model; @NonNull private int year; @NonNull private String engineCode; @NonNull private String engineType; @NonNull private String requiredFuel; @NonNull private String fuelSystem; @NonNull private String maxHorsePower; @NonNull private String maxTorque; @NonNull private float fuelCapacity; public static void main(String[] args) { DetailedCar camaroIncomplete = DetailedCar.builder() .brand("Chevrolet") .model("Camaro") .year(2022) ... .build(); } }
2. Kotlin data Sınıfı İle Kıyaslama
Kotlin de benzer bir işe yarayan data sınıfı sunuyor. Şöyle yaparız
data class Sample (val key, val value)
- Alan val olarak tanımlıysa final olur, var olarak tanımlıysa olmaz.
- data sınıfı farklı olarak copy() metodu sunuyor. 
- Üretilen data sınıfı başka bir şeyden kalıtmaz.

3. record ile Üretilen Kod
Bu üretilen kodla ilgili bazı açıklamalar şöyle. Bize her parametre için bir getter() sağlanıyor. Ayrıca toString(), equals() ve hashCode() metodları da üretiliyor.
Interestingly, similar to Enums, Records are normal Java classes with a few fundamental properties:

- They are declared as final classes, so we can’t inherit from them.
- They’re already inheriting from another class named java.lang.Record. Therefore, Records can’t extend any other class, as - Java does not allow multiple-inheritance.
- Records can implement other interfaces.
- For each component, there is an accessor method, e.g. max and min.
- There are auto-generated implementations for toString, equals and hashCode based on all components.
- Finally, there is an auto-generated constructor that accepts all components as its arguments.
record için üretilen kodu görmek için şöyle yaparız. record sınıfının ismi cls.java olsun
// Kodu derle javac path/to/cls.java // Kodu decompile et javap path/to/cls.class
3.1 Immutable Record
 record ile Üretilen Kod sadece getter() sağlasa bile döndürülen nesne tam anlamıyla Immutable değil

Örnek
Elimizde şöyle bir kod olsun. Burada record içindeki listeye yeni bir elemen ekleyebildik
public record User(String firstName, String lastName, List<String> emailAddress) { } List<String> emailAddress = new ArrayList<>(); emailAddress.add("john@doe.com"); emailAddress.add("john02@test.com"); User user = new User("John", "Doe", emailAddress); // Add new item to the email address list user.emailAddress().add("john03@gmail.com");
constructor
Normalde record için constructor yazılmasına gerek yok, ancak bazı durumlarda bu yapılabilir. Açıklaması şöyle
Records can have multiple constructors. It’s also worth noting that if you specify a custom constructor within the record, it must call the default constructor.
Örnek
Şöyle yaparız
public record PersonRecord(String name, Integer age) {    
  public PersonRecord() {
    this("Name", 18);
  }
}


// It’s acceptable if your constructor is the same as the default, 
// as long as you also initialize all of the record’s fields:
public record PersonRecord(String name, Integer age) {
  // Will replace the default constructor
  public PersonRecord(String name, Integer age) {
    this.name = name;
    this.age = age; 
  }
}
Örnek
Şöyle yaparız. Burada hem sadece tek int, hem parametresiz constructor, hem de String olan constructor tanımlanıyor.
record X(int i, int j) {
  X(int i) {
    this(i, 0);
  }
  X() {
    this(0, 0);
  }
  X(String i, String j) {
    this(Integer.parseInt(i), Integer.parseInt(j));
  }
}
Örnek - Kopyasını Alma
Şöyle yaparız. Burada parametrenin kopyası alınıyor.
record SomeRecord(Set<Thing> set) {
  public SomeRecord {
    set = Set.copyOf(set);
  }
}
Örnek - Bean Validation 
Şöyle yaparız
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;

public record EnterpriseRecord(String id, 
  @NotNull String name, 
  @NotNull @Length(min = 2, max = 255) String address) {
}
Örnek - Bean Validation 
Şöyle yaparız.
public record Film(String title, String director, int releaseYear) {
  
  public Film {
    if (Objects.isNull(title)) {
      this.title = "Unknown Title";
    }
  
    if (Objects.isNull(director)) {
      this.director = "Unknown Director";
    }
  }
}

hashCode ve equals() metodlarını Override Etmek

Açıklaması şöyle. Normalde bunu yapmaya  gerek yok. Eğer bu metodları override etmeye karar verirsek tüm alanları kullanmak gerekir.
In particular, a custom equals implementation must satisfy the expected semantic that a copy of a record must equal the record. This is not generally true for classes (e.g. two Car objects might be equals if their VIN value is the same even if owner fields are different) but must be true for records. This restriction would mean that there is rarely any reason to override equals.
Kullanım

Örnek

Şöyle yaparız.
record Point(int x, int y) {
}
Örnek
Elimizde şöyle bir kod olsun.
public record Range(int min, int max) {}
Bu kod derlenince şöyle olur.
public final class Range extends java.lang.Record {

  public Range(int, int);

  public java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);


  public int min();
  public int max();
}
Örnek
Elimizde şöyle bir kod olsun
public record ProductViewModel
        (
                String id,
                String name,
                String description,
                float price
        ) {
}
Şöyle yaparız
@Put(uri = "/{id}")
public Maybe<HttpResponse> Update(ProductViewModel model, String id) {
  ...
}
Örnek
Eğer bir metoda setter yazmak istersek şöyle yaparız ancak bu sefer kullanım amacı dışına çıkıyor.
public record ProductViewModel(String id,
                               String name, 
                               String description,
                               float price) {

  public ProductViewModel withId(String id) {
    return new ProductViewModel(id, name(), description(), price());
  }
} 
static metodlar
Açıklaması şöyle
In a Record, you may specify both non-static and static methods:
Örnek
Şöyle yaparız
public record PersonRecord(String name, Integer age) {
  public boolean isOver18() {
    return age() > 18;
  }
  public static boolean isOver18(PersonRecord personRecord) {
    return personRecord.age() > 18;
  }
}


Hiç yorum yok:

Yorum Gönder