Java 21

Paylaş

Java 21 ile gelen yenilikler String Templates, Sequenced Collections, Record Patterns, Pattern Matching for switch, Virtual Threads ve diğer özellikler ile ilgili bilgiler ve kullanımı yer alıyor.

String Templates (Preview) – JEP 430

Özellik karmaşık String ifadelerini daha güvenli, anlaşılır ve genişletilebilir şekilde kullanımı sağlar.

Java içerisinde string ifadeleri oluşturma okumayı zorlaştıran + operatörü sıklıkla kullanılır.

String s = x + " plus " + y + " equals " + (x + y);

Daha anlaşılır olarak benzer sonucu elde etmek için StringBuilder kullanımı aşağıdaki gibidir.

String s = new StringBuilder()
                 .append(x)
                 .append(" plus ")
                 .append(y)
                 .append(" equals ")
                 .append(x + y)
                 .toString();

String sınıfında yer alan format ve formatted kullanımı aşağıdaki gibidir.

String s = String.format("%2$d plus %1$d equals %3$d", x, y, x + y);
String t = "%2$d plus %1$d equals %3$d".formatted(x, y, x + y);

MessageFormat sınıfı ile kullanımı aşağıdaki gibidir.

MessageFormat mf = new MessageFormat("{0} plus {1} equals {2}");
String s = mf.format(x, y, x + y);

Kullanılan ifadeler SQL, JSON gibi farklı veri formatlarında, veri formatına özgü istenmeyen değerlerin eklenmesine olanak sağlar.

String name = "Smith' OR p.last_name <> 'Smith";
String query = "SELECT * FROM Person p WHERE p.last_name = '" + name + "'";
System.out.println(query);

String Templates özelliği java.lang.StringTemplate arayüzü ile birlikte ön tanımlı olarak STR ile birlikte gelir.

String name = "Yusuf Sezer";
String info = STR."My name is \{name}";
System.out.println(info);

Aşağıda özelliğin örnek kullanımı yer almaktadır.

// String Templates string işlemlerini gerçekleştirebilir
String firstName = "Yusuf";
String lastName  = "Sezer";
String fullName  = STR."\{firstName} \{lastName}";
String sortName  = STR."\{lastName}, \{firstName}";

// String Templates aritmetik işlem gerçekleştirebilir
int x = 10, y = 20;
String s = STR."\{x} + \{y} = \{x + y}";

// String Templates metotları çağırabilir, alanlara erişebilir
String s = STR."You have a \{getOfferType()} waiting for you!";
String t = STR."Access at \{req.date} \{req.time} from \{req.ipAddress}";

Karmaşık String ifadelerinin String Templates özelliğiyle kullanımı aşağıdaki gibidir.

String filePath = "tmp.dat";
File file = new File(filePath);
String oldMessage = "The file " + filePath + " " + (file.exists() ? "does" : "does not") + " exist";
String newMessage = STR."The file \{filePath} \{file.exists() ? "does" : "does not"} exist";
System.out.println(newMessage);

String Templates birden fazla satır içeren değerlerle kullanımı aşağıdaki gibidir.

String time = STR."The time is \{
    DateTimeFormatter
      .ofPattern("HH:mm:ss")
      .format(LocalTime.now())
} right now";

Kullanılan değerler Java komutları olabilir.

int index = 0;
String data = STR."\{index++}, \{index++}, \{index++}, \{index++}";
System.out.println(data);

String Templates iç-içe kullanılabilir.

String[] fruit = { "apples", "oranges", "peaches" };
String s = STR."\{fruit[0]}, \{STR."\{fruit[1]}, \{fruit[2]}"}";
System.out.println(s);

Daha anlaşılır olması için ayrılabilir.

String tmp = STR."\{fruit[1]}, \{fruit[2]}";
String s = STR."\{fruit[0]}, \{tmp}";

String Templates Java 13 ile birlikte gelen Text Blocks ile birlikte kullanılabilir.

String title = "My Web Page";
String text  = "Hello, world";
String html = STR."""
        <html>
          <head>
            <title>\{title}</title>
          </head>
          <body>
            <p>\{text}</p>
          </body>
        </html>
        """;
// <html>
//   <head>
//     <title>My Web Page</title>
//   </head>
//   <body>
//     <p>Hello, world</p>
//   </body>
// </html>

String name    = "Joan Smith";
String phone   = "555-123-4567";
String address = "1 Maple Drive, Anytown";
String json = STR."""
    {
        "name":    "\{name}",
        "phone":   "\{phone}",
        "address": "\{address}"
    }
    """;

// {
//     "name":    "Joan Smith",
//     "phone":   "555-123-4567",
//     "address": "1 Maple Drive, Anytown"
// }

record Rectangle(String name, double width, double height) {
    double area() {
        return width * height;
    }
}
Rectangle[] zone = new Rectangle[] {
    new Rectangle("Alfa", 17.8, 31.4),
    new Rectangle("Bravo", 9.6, 12.4),
    new Rectangle("Charlie", 7.1, 11.23),
};
String table = STR."""
    Description  Width  Height  Area
    \{zone[0].name}  \{zone[0].width}  \{zone[0].height}     \{zone[0].area()}
    \{zone[1].name}  \{zone[1].width}  \{zone[1].height}     \{zone[1].area()}
    \{zone[2].name}  \{zone[2].width}  \{zone[2].height}     \{zone[2].area()}
    Total \{zone[0].area() + zone[1].area() + zone[2].area()}
    """;

// Description  Width  Height  Area
// Alfa  17.8  31.4     558.92
// Bravo  9.6  12.4     119.03999999999999
// Charlie  7.1  11.23     79.733
// Total 757.693

String Templates özelliği genişletilebilirliği sayesinde ön tanımlı FMT ön eki ile biçimlendirilmiş olarak kullanılabilir.

record Rectangle(String name, double width, double height) {
    double area() {
        return width * height;
    }
}
Rectangle[] zone = new Rectangle[] {
    new Rectangle("Alfa", 17.8, 31.4),
    new Rectangle("Bravo", 9.6, 12.4),
    new Rectangle("Charlie", 7.1, 11.23),
};
String table = FMT."""
    Description     Width    Height     Area
    %-12s\{zone[0].name}  %7.2f\{zone[0].width}  %7.2f\{zone[0].height}     %7.2f\{zone[0].area()}
    %-12s\{zone[1].name}  %7.2f\{zone[1].width}  %7.2f\{zone[1].height}     %7.2f\{zone[1].area()}
    %-12s\{zone[2].name}  %7.2f\{zone[2].width}  %7.2f\{zone[2].height}     %7.2f\{zone[2].area()}
    \{" ".repeat(28)} Total %7.2f\{zone[0].area() + zone[1].area() + zone[2].area()}
    """;

// Description     Width    Height     Area
// Alfa            17.80    31.40      558.92
// Bravo            9.60    12.40      119.04
// Charlie          7.10    11.23       79.73
//                              Total  757.69

String Templates özelliği java.lang.StringTemplate.Processor arayüzü sayesinde özel işleç tanımlanabilir.

package java.lang;
public interface StringTemplate {
    ...
    @FunctionalInterface
    public interface Processor<R, E extends Throwable> {
        R process(StringTemplate st) throws E;
    }
    ...
}

Örnek kullanım aşağıdaki gibidir.

var INTER = StringTemplate.Processor.of((StringTemplate st) -> {
    String placeHolder = "•";
    String stencil = String.join(placeHolder, st.fragments());
    for (Object value : st.values()) {
        String v = String.valueOf(value);
        stencil = stencil.replaceFirst(placeHolder, v);
    }
    return stencil;
});

int x = 10, y = 20;
String s = INTER."\{x} plus \{y} equals \{x + y}";

Özel JSON işleci aşağıdaki gibi yapılabilir.

var JSON = StringTemplate.Processor.of(
        (StringTemplate st) -> new JSONObject(st.interpolate())
    );

String name    = "Joan Smith";
String phone   = "555-123-4567";
String address = "1 Maple Drive, Anytown";
JSONObject doc = JSON."""
    {
        "name":    "\{name}",
        "phone":   "\{phone}",
        "address": "\{address}"
    };
    """;

Özel SQL işleci aşağıdaki gibi yapılabilir.

record QueryBuilder(Connection conn)
  implements StringTemplate.Processor<PreparedStatement, SQLException> {

    public PreparedStatement process(StringTemplate st) throws SQLException {
        // 1. Replace StringTemplate placeholders with PreparedStatement placeholders
        String query = String.join("?", st.fragments());

        // 2. Create the PreparedStatement on the connection
        PreparedStatement ps = conn.prepareStatement(query);

        // 3. Set parameters of the PreparedStatement
        int index = 1;
        for (Object value : st.values()) {
            switch (value) {
                case Integer i -> ps.setInt(index++, i);
                case Float f   -> ps.setFloat(index++, f);
                case Double d  -> ps.setDouble(index++, d);
                case Boolean b -> ps.setBoolean(index++, b);
                default        -> ps.setString(index++, String.valueOf(value));
            }
        }

        return ps;
    }
}

Kullanımı aşağıdaki gibidir.

var DB = new QueryBuilder(conn);
PreparedStatement ps = DB."SELECT * FROM Person p WHERE p.last_name = \{name}";
ResultSet rs = ps.executeQuery();

String Templates özelliği sağladığı arayüzler sayesinde genişletilebilirliği sağlamaktadır.

NOT: Geliştirilen özel işleçler (SQL, JSON, XML) sadece ifadeleri işlemek üzere kullanılmalıdır.

Sequenced Collections – JEP 431

Sequenced Collections özelliği ile birlikte gelen SequencedCollection, SequencedSet, SequencedMap arayüzleri Java koleksiyonlarına eklenerek koleksiyondaki ilk, son elemanlara erişim kolay hale gelmiştir.

interface SequencedCollection<E> extends Collection<E> {
    // new method
    SequencedCollection<E> reversed();
    // methods promoted from Deque
    void addFirst(E);
    void addLast(E);
    E getFirst();
    E getLast();
    E removeFirst();
    E removeLast();
}
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
    SequencedSet<E> reversed();    // covariant override
}
interface SequencedMap<K,V> extends Map<K,V> {
    // new methods
    SequencedMap<K,V> reversed();
    SequencedSet<K> sequencedKeySet();
    SequencedCollection<V> sequencedValues();
    SequencedSet<Entry<K,V>> sequencedEntrySet();
    V putFirst(K, V);
    V putLast(K, V);
    // methods promoted from NavigableMap
    Entry<K, V> firstEntry();
    Entry<K, V> lastEntry();
    Entry<K, V> pollFirstEntry();
    Entry<K, V> pollLastEntry();
}

Koleksiyonundaki ilk elemana erişim örneği aşağıdaki gibidir.

List<String> names = List.of("Yusuf", "sefa", "Sezer");
// System.out.println(names.get(0));
System.out.println(names.getFirst());
// System.out.println(names.get(names.size() - 1));
System.out.println(names.getLast());

Özellikle birlikte Collections yardımcı sınıfına unmodifiableSequencedCollection, unmodifiableSequencedSet, unmodifiableSequencedMap metotları eklenmiştir.

Generational ZGC – JEP 439

ZGC çöp toplayıcına genç ve yaşlı olarak ifade belirlenen nesiller özelliği eklenerek Java uygulamalarındaki performans artışı sağlamıştır.

Record Patterns – JEP 440

Java 19 ile birlikte gelen Record Patterns özelliğe Java 20 ile birlikte for döngüsü desteği eklenmiştir.

Record Patterns özelliği standart olarak dile Java 20 sürümünde gelen for döngüsü özelliği olmadan eklenmiştir.

Pattern Matching for switch – JEP 441

Java 17 ile birlikte gelen özelliğine Java 18, Java 19 ve Java 20 sürümlerinde geliştirilmeye devam edilerek Java 21 ile birlikte enum desteği genişletilerek Java standardı olmuştur.

Foreign Function & Memory API (Third Preview) – JEP 442

Java 14 ile birlikte Java 15, Java 16, Java 17, Java 18, Java 19, Java 20 ile geliştirilmesi devam edilen Java tarafından yönetilmeyen bellek alanlarına erişimi sağlayan Foreign Function & Memory API (FFM API) geliştirilmeye devam etmektedir.

Unnamed Patterns and Variables (Preview) – JEP 443

Record Patterns, Java döngüleri, try-with-resources bloklarında kullanılmayacak olsa bile zorunlu değişken tanımı yapılması kodların karmaşıklaşmasına neden olmaktadır.

Unnamed Patterns and Variables özelliği kullanılmayacak değişkenleri alt çizgi (_) ile belirleyerek kod karmaşıklığını gidermeyi sağlayan bir Java özelliğidir.

int acc = 0;
for (Order _ : orders) {
    if (acc < LIMIT) { 
        ... acc++ ...
    }
}
Queue<Integer> q = ... // x1, y1, z1, x2, y2, z2, ...
while (q.size() >= 3) {
   var x = q.remove();
   var y = q.remove();
   var _ = q.remove();
   ... new Point(x, y) ...
}

Unnamed Patterns and Variables özelliğinin try-with-resources bloğunda kullanımı.

String s = ...
try { 
    int i = Integer.parseInt(s);
    ... i ...
} catch (NumberFormatException _) { 
    System.out.println("Bad number: " + s);
}

Virtual Threads – JEP 444

Java 19 ile birlikte gelen ve Java 20 sürümüyle geliştirilmeye devam eden Virtual Threads özelliği Java 21 ile birlikte standardı olmuştur.

Unnamed Classes and Instance Main Methods (Preview) – JEP 445

Java diline yeni başlayanların standart ve uzun Java kodlarını yazmadan Java kodları yazmasını amaçlayan özelliktir.

Java Nedir? yazımda yer alan Merhava Java örneğine bakalım.

public class MerhabaJava {

    public static void main(String[] args) {
        System.out.println("Merhaba Java");
    }

}

Örnekte class ile sını tanımı, public ile erişim belirteci, String[] ile argüman dizileri ve static ile statik belirtilmiştir.

Instance Main Methods özelliğiyle daha sade aşağıdaki gibi yazılabilir.

class MerhabaJava { 
    void main() { 
        System.out.println("Merhaba Java");
    }
}

Unnamed Classes özelliğiyle daha da sade aşağıdaki gibi yazılabilir.

void main() {
    System.out.println("Merhaba Java");
}

Özelliğin kullanımı için –enable-preview parametresinin derleyiciye eklenmesi gerekmektedir.

java --enable-preview MerhabaJava.java
// veya
javac --release 21 --enable-preview MerhabaJava.java

Scoped Values (Preview) – JEP 446

Java 20 ile birlikte gelen Scoped Values özelliği geliştirilmeye devam etmektedir.

Vector API (Sixth Incubator) – JEP 448

Java 16 ile birlikte gelen Java 17, Java 18, Java 19, Java 20 ile devam Vector API geliştirilmeye devam etmektedir.

Özellik içsel olarak Java uygulamalarını daha performanslı çalıştırmak için yapılan güncelleme olduğundan sınıf, paket tarafında herhangi bir değişiklik yapılmamaktadır.

Deprecate the Windows 32-bit x86 Port for Removal – JEP 449

Windows 32-bit x86 işletim sistemi desteği deprecated olarak belirtilerek gelecek sürümlerde kaldırılacaktır.

Prepare to Disallow the Dynamic Loading of Agents – JEP 451

Çalışan JVM üzerine Java Agentları çalıştırmayı engelleyen özelliktir. Özellik sayesinde çalışan JVM üzerinde dinamik olarak değişiklik yapılmasının önüne geçilerek istenmeyen durumların önlenmesi amaçlanmıştır.

Key Encapsulation Mechanism API – JEP 452

KEM kriptoloji algoritmalarının (RSA-KEM, ECIES, NIST) kullanımı için geliştirilen API’dir.

package javax.crypto;

public class DecapsulateException extends GeneralSecurityException;

public final class KEM {

    public static KEM getInstance(String alg)
        throws NoSuchAlgorithmException;
    public static KEM getInstance(String alg, Provider p)
        throws NoSuchAlgorithmException;
    public static KEM getInstance(String alg, String p)
        throws NoSuchAlgorithmException, NoSuchProviderException;

    public static final class Encapsulated {
        public Encapsulated(SecretKey key, byte[] encapsulation, byte[] params);
        public SecretKey key();
        public byte[] encapsulation();
        public byte[] params();
    }

    public static final class Encapsulator {
        String providerName();
        int secretSize();           // Size of the shared secret
        int encapsulationSize();    // Size of the key encapsulation message
        Encapsulated encapsulate();
        Encapsulated encapsulate(int from, int to, String algorithm);
    }

    public Encapsulator newEncapsulator(PublicKey pk)
            throws InvalidKeyException;
    public Encapsulator newEncapsulator(PublicKey pk, SecureRandom sr)
            throws InvalidKeyException;
    public Encapsulator newEncapsulator(PublicKey pk, AlgorithmParameterSpec spec,
                                        SecureRandom sr)
            throws InvalidAlgorithmParameterException, InvalidKeyException;

    public static final class Decapsulator {
        String providerName();
        int secretSize();           // Size of the shared secret
        int encapsulationSize();    // Size of the key encapsulation message
        SecretKey decapsulate(byte[] encapsulation) throws DecapsulateException;
        SecretKey decapsulate(byte[] encapsulation, int from, int to,
                              String algorithm)
                throws DecapsulateException;
    }

    public Decapsulator newDecapsulator(PrivateKey sk)
            throws InvalidKeyException;
    public Decapsulator newDecapsulator(PrivateKey sk, AlgorithmParameterSpec spec)
            throws InvalidAlgorithmParameterException, InvalidKeyException;

}

KEM API kullanım örneği aşağıda yer almaktadır.

// Receiver side
var kpg = KeyPairGenerator.getInstance("X25519");
var kp = kpg.generateKeyPair();

// Sender side
var kem1 = KEM.getInstance("DHKEM");
var sender = kem1.newEncapsulator(kp.getPublic());
var encapsulated = sender.encapsulate();
var k1 = encapsulated.key();

// Receiver side
var kem2 = KEM.getInstance("DHKEM");
var receiver = kem2.newDecapsulator(kp.getPrivate());
var k2 = receiver.decapsulate(encapsulated.encapsulation());

boolean result = Arrays.equals(k1.getEncoded(), k2.getEncoded());

System.out.println(result);

Structured Concurrency (Preview) – JEP 453

Java 19 ile birlikte gelen Java 20 ile geliştirilmeye devam Structured Concurrency özelliği geliştirilmeye devam etmektedir.

Diğer

Java 9 ile birlikte sürekli olarak güncellenen Java platformu Java 17’den sonraki LTS yani Uzun Süreli Destek sürümüdür.

Java 21 sürümünde önceki Java sürümlerinde gelen özelliklerin bir çoğu Java standardı olarak Java diline eklenmiştir.

Java Derslerine buradan ulaşabilirsiniz.

Hayırlı günler dilerim.


Bunlarda ilgini çekebilir


LinkedIn grubuna buradan katılın.