SlideShare a Scribd company logo
1 of 53
Download to read offline
深入淺出 Java 21 功能
Joseph Kuo (CyberJos)
VP of Software Development @
NextDrive
如果有人能簡介
Java 21 功能的
話那該有多好!
About Me
• 1993 年學程式、1999 年 Java 1.2
• 主修應數、副修資工
• 資訊教育機構、加值簡訊服務、雲端影音平台、
全球電子商務、資訊安全防護、用戶行為管理、
智慧物聯網平台
• 講師經歷:JCConf、Oracle Groundbreakers、
AWS Summit、……
• 系統架構、雲端運算、邊緣運算、後端技術、軟
工設計、智慧物聯
• 希望能一輩子玩技術寫程式到老 2
https://cyberjos.blog/
Taiwan Java User Group
• FB:https://www.facebook.com/groups/twjug
• 官方網站:http://www.twjug.org/
• LINE 社群「Java 程式語言討論區」
3
歡迎來到 JCConf 2023
4
Virtual Threads
Spring Framework
Kotlin
JVM
Jakarta EE
GraalVM
Scala
Android
Redis
MySQL
VMware
Lombok Javalin
AWS
Azure
LINE
Autopass
Java 21 LTS
SoftLeader
Oracle
2023 State of Java Ecosystem
2023 State of Java Ecosystem
• New Relic first published in March 2020
• 2023 state was published in April 2023
• Based on data gathered from millions of
applications providing performance data
• The data for this report was drawn entirely
from applications reporting to New Relic in
January 2023 and does not provide a global
picture of Java usage.
6
Source: https://newrelic.com/resources/report/2023-state-of-java-ecosystem
LTS 中 Java 17 用戶增長率顯著
7
非 LTS 版本仍以 14 穩居第一
8
Amazon 躍升 JDK 供應商龍頭
9
G1GC 維持 Java 11 後領先地位
10
Java 21 有哪些新功能?
11
聽說 Java 21 是下個 LTS 版
本,它有哪些新功能呢?
我們需要開始使用它嗎?
好,我們完成系統轉換到
Java 11 平台,甚至有一些
舊服務以及服務已經採用
Java 17 開發了……
我有故事,你有酒嗎?
早在 Java 遠古時代 (< Java 7)
String text = "NULL";
if (obj instanceof String) { // check
String s = (String) obj; // assign + cast
text = "String: " + s;
} else if (obj instanceof Integer) {
Integer i = (Integer) obj;
if (i < 10000) text = "Small Int: " + i;
else text = "Big Int: " + i;
} else if (obj instanceof Long) {
Long l = (Long) obj;
text = "Long: " + l;
} else if (obj instanceof Number) {
Number n = (Number) obj;
text = "Number: " + n;
} else if (obj != null)
text = "Object: " + obj; 13
Java 16 加強了 instanceof
String text = "NULL";
if (obj instanceof String s) { // check + assign + cast
text = "String: " + s;
} else if (obj instanceof Integer i && i < 10_000) {
text = "Small Int: " + i;
} else if (obj instanceof Integer i) {
text = "Big Int: " + i;
} else if (obj instanceof Long l) {
text = "Long: " + l;
} else if (obj instanceof Number n) {
text = "Number: " + n;
} else if (obj != null) {
text = "Object: " + obj;
}
14
雖然簡化了,但還
是有很多判斷式!
何不用 switch?
15
Pattern Matching for switch
String text = switch (obj) {
case null -> "NULL"; // null check
case String s -> "String: " + s;
case Integer i
when i < 10_000 -> "Small Int: " + i;
case Integer i -> "Big Int: " + i;
case Long l -> "Long: " + l;
case Number n -> "Number: " + n;
default -> "Object: " + obj;
}
16
1. 可以檢查 null
2. 可以用 when 來增加判斷式
(guarded pattern)
3. Java14 讓 switch 可以回
傳值並設定 text 變數內容
注意!上下順序很重要!
String text = switch (obj) {
case null -> "NULL";
case String s -> "String: " + s;
case Number n -> "Number: " + n;
// Error: this case label is dominated
// by a preceding case label
case Integer i -> "Integer: " + i; // unreachable
case Long l -> "Long: " + l;
default -> "Object: " + obj;
}
17
到不了的地方叫遠方
支配權 Dominance
• 父類別大於子類別 (例:Number > Integer)
• Unguarded pattern 大於 guarded pattern
(case Type > case Type when expr.)
• Guarded pattern when true 大於其他
pattern(guarded & unguarded)
• Pattern 大於常數(Integer i > 1, 2)
• Enum 類別大於常數(Color > Color.RED)
• when 不會進行過多的額外檢查
18
建議撰寫原則
• 先寫 case null (純粹為了易讀性與維護性)
• 次寫 case 常數和列舉常數
• 再寫 guarded 的 case Type t when 判斷式
• 後寫 unguarded 的 case Type t
• 特別注意 Type 的子類別要放在父類別之前
• 最後寫 default 做收尾(若未窮舉的話)
19
搭配 enum 一起用
enum Color { RED, GREEN, BLUE, YELLOW, PURPLE }
String text = switch (obj) {
case null -> "NULL";
case Color.BLUE -> "Color.BLUE"; // 短版本
case Color c
when c == Color.RED -> "Color.RED";// 與上式等價
case Color c -> "Color: others"; // 注意支配性
case int[] ia -> "Int array: " + ia; // 陣列也可以
default -> "Object: " + obj;
}
20
記得 enum、sealed 需要注
意是否要窮舉所有可能值!
Record 也可以加入嗎?
class Point { // immutable
private final int x, y;
public Point(int x, int y) { this.x=x; this.y=y; }
public int x() { return x; }
public int y() { return y; }
public boolean equals(Object o) {
return o instanceof Point
&& ((Point) o).x == x
&& ((Point) o).y == y;
}
public int hashCode() { return Objects.hash(x, y); }
public String toString() {
return String.format("Point[x=%d, y=%d]", x, y);
}
}
回到 Java 上古時代(= Java 7)
22
Java16 record + Java17 sealed
23
record Point(int x, int y) {} // 上頁的程式碼濃縮成一行了
// 順便增加一些複雜度
sealed interface Shape
permits Circle, Rectangle, Triangle {
public static void check(Point p) {
if (p.x() <= 0)
throw new IllegalArgumentException("NG!");
}
}
record Circle(Point p, int r, Color c)
implements Shape {}
record Rectangle(Point p1, Point p2, Color c)
implements Shape {}
record Triangle(Point p1, Point p2, Point p3, Color c)
implements Shape {}
return switch (obj) {
case null -> "NULL";
case Point p -> "Point: " + p; // Pattern matching
case Circle(Point p, int r, Color c) // record pattern
-> "Circle: p:" + p;
case Rectangle(Point p1, Point p2, Color c)
-> "Rectangle: 1:" + p1 + ", 2:" + p2;
case Triangle(Point p1, Point p2, Point p3, Color c)
-> "Triangle: 1:" + p1 + ", 2:" + p2 + ", 3:" + p3;
default -> "Object: " + obj;
};
24
這樣一來就不需要使用
case Circle c -> c.p();
Record Pattern
record Pair<T>(T a, T b) {}
private Pair<Shape> o;
switch (o) {
// 要窮舉所有可能值
case Pair<Shape>(Circle c, Shape s) -> ...
case Pair<Shape>(Rectangle r, Circle c) -> ...
case Pair<Shape>(Rectangle r, Rectangle r) -> ...
case Pair<Shape>(Rectangle r, Triangle t) -> ...
case Pair<Shape>(Triangle t, Shape s) -> ...
// 或是用下列任一行收尾
case Pair<Shape>(var a, var b) -> ...
case Pair<Shape> x -> ...
default -> ...
}
25
要注意是否已窮舉
If record in if
// Java 16 ~ 20
if (o instanceof Point p) {
int x = p.x();
int y = p.y();
System.out.println(x + y);
}
// Java 21 Record pattern
if (o instanceof Point(int x, int y)) {
System.out.println(x + y);
}
26
更複雜的 Record Pattern
if (o instanceof Circle(Point(int x, int y), int r, Color c)) {
System.out.println(x + y);
}
if (o instanceof Rectangle(Point(var x,var y), var p2, var c)) {
System.out.println(x); // 也可以使用 var
}
if (o instanceof Triangle(Point(int x1, int y1),
Point(var x2, var y2), Point p3, var c)) {
System.out.println(c);
}
27
程式碼能夠
簡化嗎?
Unnamed Patterns (Preview)
if (o instanceof Circle(Point(int x, int y), int _, Color _) {
System.out.println(x + y); // unnamed pattern variable
}
if (o instanceof Rectangle(Point(var x, _), _, _) {
System.out.println(x); // unnamed pattern (Type v)
}
if (o instanceof Triangle(_, _, _, var c) { // unnamed pattern
System.out.println(c);
}
if (o instanceof Point _) { // unnamed pattern variable
System.out.println("Point!");
}
28
程式碼複雜度降
低,可讀性增加!
Unnamed Variables (Preview)
var points = new ArrayList<Point>(List.of(p1, p2, p3, p4, p5));
for (Point _ : points) { // enhanced for loop
if (DEBUG) count++;
}
for (int i = 0, _ = init(); i < count; i++) { // for statement
try (var _ = getRes()) { // try-with-resources statement
Point p = points.getFirst();
Shape.check(p);
m.putAll(Arrays.stream(Color.values())
.map(c -> new Circle(p, i, c))
.collect(toMap(Function.identity(),
_ -> 1L)) //lambda
);
} catch (IllegalArgumentException _) { // exception
var _ = points.removeFirst(); // local variable
}
}
29
這是禁止事項 🙅
• o instanceof _
• o instanceof _(int x, int y)
• o instanceof var _
• case _
• case var _
• case Type(_)
• void test(String x, int _) {}
• ...
30
剛剛好像有看到 list 的
getFirst / removeFirst?
各自表述,沒有共識
First element Last element
List list.get(0) list.get(list.size() - 1)
Deque deque.getFirst() deque.getLast()
SortedSet set.first() set.last()
LinkedHashSet set.iterator().next() // missing
32
不同集合取得
首尾元素有不
同做法……
Sequenced Collections
• 增加 Collections 的學習曲線和複雜度(X)
• 加強集合元素的序列處理能力(O)
• 引入新的介面和方法來簡化有序集合
• 提供全員一致的方法來存取首尾元素
• 同時提供反序集合的生成方法
33
Sequenced Collections 繼承樹
SequencedCollection
// new method
SequencedCollection<E> reversed();
// methods promoted from Deque
void addFirst(E);
void addLast(E);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
// add*(E) & remove*() are optional (unmodifiable)
// get*() & remove*() throw NoSuchElementEx if empty
35
SequencedSet
// covariant override
SequencedSet<E> reversed();
// add*(E) throw UnsupportedOperationEx in SortedSet
36
臣妾做不到啊……
SequencedMap
// new method
SequencedMap<K, V> reversed();
SequencedSet<K> sequencedKeySet();
SequencedCollection<V> sequencedValues();
SequencedSet<Entry<K, V>> sequencedEntrySet();
V putFirst(K, V);
V putLast(K, V);
// put*(K, V) and poll*(K, V) are optional
// put*(K, V) throw UnsupportedOperationEx in SortedMap
// methods promoted from NavigableMap
Entry<K, V> firstEntry();
Entry<K, V> lastEntry();
Entry<K, V> pollFirstEntry();
Entry<K, V> pollLastEntry();
37
Virtual Threads
• 繼承 Thread 的輕量級執行緒,可大幅減少撰
寫、維護和監控高吞吐並行程式的工作量
• 用 thread-per-request 風格撰寫的服務器應用
程式能以接近最佳化的硬體使用率來進行擴容
• 原本使用 Thread API 的既有程式碼能在最小的
改動下採用虛擬執行緒
• 與既有的 JDK 工具整合,使其易於使用、除錯
和量測
38
Thread-per-request 風格
• 服務器程式利用執行緒去完全處理每個請求
• Little's Law(L=λW):給定的請求處理時間
內(延遲λ),服務器同時能處理的請求數(並
行L )與抵達率(吞吐W)成比例增長
• 綜合上述,增加吞吐量代表執行緒數量也要增
加,才能在維持相同的延遲
• 但執行緒的數量有其限制,因為 JDK 的平台執
行緒對應的是代價昂貴的 OS 執行緒
39
另外有非
同步風格
改進 thread-per-request 效率
• 為了在擴展應用程式的同時能與平台維持協作,
所以用更有效率的方式來實作執行緒
• 如同 OS 將大量虛擬定址空間映射到有限實體
RAM 一樣,JVM 將大量虛擬執行緒映射到少
量的 OS 執行緒,達成增加執行緒數量
• 虛擬執行緒會被臨時對映到載體執行緒中,一
旦遇到阻塞操作,就會從載體中移除,而載體
可以執行另一個虛擬執行緒(新的或之前被阻
塞的)
40
虛擬執行緒要怎麼用?
try (var executor =
Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000)
.forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}
1. 無負擔建立虛擬執行緒
2. 可以設定較大的數量
3. 背後只用到少量OS執行緒
4. 如果用平台執行緒的話會出事
41
你也許感興趣的事情
• 虛擬執行緒不會加快執行速度,事實上它與平
台執行緒的速度一致。它的目的是為了提供更
高的吞吐量,而不是更低的延遲
• 適用於並行數量多且非 CPU 負載時(I/O)
• 不要用執行緒池,因為它便宜且大量,主打DB
查詢或 HTTP 請求。平台執行緒很昂貴,所以
才需要執行緒池保持其長壽並方便調度
• 小心 ThreadLocal,建議用 ScopedValue
• Structured Concurrency 預設使用虛擬執行緒
42
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
回到 Java 盤古開天時代說哈囉
1. 程式碼太長
2. 需解釋太多觀念
3. 太多不需要的結構
4. 有點太過雜亂
5. 對新手不友善
43
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
Instance Main Methods(Preview)
1. main 方法不需要是 static
2. 也不需要 public
3. 不需要有 String[] 傳入值
44
1. 類別中非 private 的
static void main(String[] args)
2. 類別中非 private 的
static void main()
3. 類別中本身宣告或繼承自父類別的
非 private 的 void main(String[] args)
4. 類別中本身宣告或繼承自父類別的
非 private 的 void main()
如何挑選?
45
void main() {
System.out.println("Hello, World!");
}
// T.java -> final class T extends Object implements
Unnamed Classes (Preview)
1. 讓 class 宣告變成可有可無
2. 當方法和欄位沒有被類別宣告包覆
時,會自動將其視為未命名類的成員
3. 不能被其他類別引用,故需要 main(),
也無法外部呼叫靜態成員與建構子
4. 不能實作介面
46
private String hello = "Hello";
String name() { return "World"; }
void main() {
System.out.println(hello + ", " + name() + "!");
System.out.println(STR."{hello}, {name()}!");
}
未命名類別內可以網內互打
了解!不過 STR 和
{} 是什麼東西?
47
String Templates (Preview)
// STR 是自動import到所有原始檔的 public static final 欄位
int x = 10, y = 20;
STR."{x} + {y} = {x + y}"; // 10 + 20 = 30
// 可呼叫方法和存取欄位
STR."{req.getTime()} from {req.ipAddress}";
// 雙引號不需要跳脫字元
STR."{file} {file.exists() ? "does" : "doesn't"} exist";
// 可放入各式運算式,包括 STR 本身,變成套嵌俄羅斯娃娃
STR."{array[index++]}, {STR."{array[index++]}"}";
// 當然,也支援 String Block
STR.""" {
"name": "{record.name}",
"phone": "{record.phone}"
"""; 48
Java 21 新功能
Core Java Library
• JEP 431: Sequenced Collections
• JEP 442: Foreign Function & Memory API
(Third Preview)
• JEP 444: Virtual Threads
• JEP 446: Scoped Values (Preview)
• JEP 448: Vector API (Sixth Incubator)
• JEP 453: Structured Concurrency (Preview)
50
Java Language Specification
• JEP 430: String Templates (Preview)
• JEP 440: Record Patterns
• JEP 441: Pattern Matching for switch
• JEP 443: Unnamed Patterns and Variables
(Preview)
• JEP 445: Unnamed Classes and Instance
Main Methods (Preview)
51
HotSpot & Security
• JEP 439: Generational ZGC
• JEP 449: Deprecate the Windows 32-bit x86
Port for Removal
• JEP 451: Prepare to Disallow the Dynamic
Loading of Agents
• JEP 452: Key Encapsulation Mechanism API
• NOTE THAT JEP 404, Generational
Shenandoah (Experimental), originally
targeted for 21, was officially removed
52
53
Thank You!!

More Related Content

What's hot

Interface java
Interface java Interface java
Interface java atiafyrose
 
Roy Osherove on Unit Testing Good Practices and Horrible Mistakes
Roy Osherove on Unit Testing Good Practices and Horrible MistakesRoy Osherove on Unit Testing Good Practices and Horrible Mistakes
Roy Osherove on Unit Testing Good Practices and Horrible MistakesRoy Osherove
 
Java Tutorial | Java Programming Tutorial | Java Basics | Java Training | Edu...
Java Tutorial | Java Programming Tutorial | Java Basics | Java Training | Edu...Java Tutorial | Java Programming Tutorial | Java Basics | Java Training | Edu...
Java Tutorial | Java Programming Tutorial | Java Basics | Java Training | Edu...Edureka!
 
Java Programming | Java Tutorial For Beginners | Java Training | Edureka
Java Programming | Java Tutorial For Beginners | Java Training | EdurekaJava Programming | Java Tutorial For Beginners | Java Training | Edureka
Java Programming | Java Tutorial For Beginners | Java Training | EdurekaEdureka!
 
Java Training | Java Tutorial for Beginners | Java Programming | Java Certifi...
Java Training | Java Tutorial for Beginners | Java Programming | Java Certifi...Java Training | Java Tutorial for Beginners | Java Programming | Java Certifi...
Java Training | Java Tutorial for Beginners | Java Programming | Java Certifi...Edureka!
 
1.9. minimization of dfa
1.9. minimization of dfa1.9. minimization of dfa
1.9. minimization of dfaSampath Kumar S
 
JRE , JDK and platform independent nature of JAVA
JRE , JDK and platform independent nature of JAVAJRE , JDK and platform independent nature of JAVA
JRE , JDK and platform independent nature of JAVAMehak Tawakley
 
Unit and integration Testing
Unit and integration TestingUnit and integration Testing
Unit and integration TestingDavid Berliner
 
Switch statements in Java
Switch statements  in JavaSwitch statements  in Java
Switch statements in JavaJin Castor
 
Exception handling in Java
Exception handling in JavaException handling in Java
Exception handling in JavaAnkit Rai
 
C++ Object Oriented Programming
C++  Object Oriented ProgrammingC++  Object Oriented Programming
C++ Object Oriented ProgrammingGamindu Udayanga
 

What's hot (20)

Introduction To Java.
Introduction To Java.Introduction To Java.
Introduction To Java.
 
DSA 103 Object Oriented Programming :: Week 3
DSA 103 Object Oriented Programming :: Week 3DSA 103 Object Oriented Programming :: Week 3
DSA 103 Object Oriented Programming :: Week 3
 
Interface java
Interface java Interface java
Interface java
 
Roy Osherove on Unit Testing Good Practices and Horrible Mistakes
Roy Osherove on Unit Testing Good Practices and Horrible MistakesRoy Osherove on Unit Testing Good Practices and Horrible Mistakes
Roy Osherove on Unit Testing Good Practices and Horrible Mistakes
 
Java Tutorial | Java Programming Tutorial | Java Basics | Java Training | Edu...
Java Tutorial | Java Programming Tutorial | Java Basics | Java Training | Edu...Java Tutorial | Java Programming Tutorial | Java Basics | Java Training | Edu...
Java Tutorial | Java Programming Tutorial | Java Basics | Java Training | Edu...
 
Java Programming | Java Tutorial For Beginners | Java Training | Edureka
Java Programming | Java Tutorial For Beginners | Java Training | EdurekaJava Programming | Java Tutorial For Beginners | Java Training | Edureka
Java Programming | Java Tutorial For Beginners | Java Training | Edureka
 
Java Training | Java Tutorial for Beginners | Java Programming | Java Certifi...
Java Training | Java Tutorial for Beginners | Java Programming | Java Certifi...Java Training | Java Tutorial for Beginners | Java Programming | Java Certifi...
Java Training | Java Tutorial for Beginners | Java Programming | Java Certifi...
 
1.9. minimization of dfa
1.9. minimization of dfa1.9. minimization of dfa
1.9. minimization of dfa
 
Java features
Java  features Java  features
Java features
 
Operators in java
Operators in javaOperators in java
Operators in java
 
JRE , JDK and platform independent nature of JAVA
JRE , JDK and platform independent nature of JAVAJRE , JDK and platform independent nature of JAVA
JRE , JDK and platform independent nature of JAVA
 
Unit and integration Testing
Unit and integration TestingUnit and integration Testing
Unit and integration Testing
 
Theory of computation Lec3 dfa
Theory of computation Lec3 dfaTheory of computation Lec3 dfa
Theory of computation Lec3 dfa
 
Java loops
Java loopsJava loops
Java loops
 
Switch statements in Java
Switch statements  in JavaSwitch statements  in Java
Switch statements in Java
 
Core java complete notes - Contact at +91-814-614-5674
Core java complete notes - Contact at +91-814-614-5674Core java complete notes - Contact at +91-814-614-5674
Core java complete notes - Contact at +91-814-614-5674
 
Exception handling in Java
Exception handling in JavaException handling in Java
Exception handling in Java
 
C++ Object Oriented Programming
C++  Object Oriented ProgrammingC++  Object Oriented Programming
C++ Object Oriented Programming
 
What is java?
What is java? What is java?
What is java?
 
JavaFX Presentation
JavaFX PresentationJavaFX Presentation
JavaFX Presentation
 

Similar to JCConf 2023 - 深入淺出 Java 21 功能

第三章 栈和队列
第三章 栈和队列第三章 栈和队列
第三章 栈和队列Wang Yizhe
 
Swift 程序语言介绍
Swift 程序语言介绍Swift 程序语言介绍
Swift 程序语言介绍明 李
 
Standford 2015 iOS讀書會 week2: 1. Applying MVC 2. More Swift and Foundation Fra...
Standford 2015 iOS讀書會 week2: 1. Applying MVC 2. More Swift and Foundation Fra...Standford 2015 iOS讀書會 week2: 1. Applying MVC 2. More Swift and Foundation Fra...
Standford 2015 iOS讀書會 week2: 1. Applying MVC 2. More Swift and Foundation Fra...彼得潘 Pan
 
第三章 栈和队列(新)
第三章 栈和队列(新)第三章 栈和队列(新)
第三章 栈和队列(新)Wang Yizhe
 
Arduino L2
Arduino L2Arduino L2
Arduino L2mmiwwcom
 
C程式-函式與巨集
C程式-函式與巨集C程式-函式與巨集
C程式-函式與巨集艾鍗科技
 
Python学习笔记
Python学习笔记Python学习笔记
Python学习笔记Lingfei Kong
 
Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Harvey Zhang
 
lambda/closure – JavaScript、Python、Scala 到 Java SE 7
lambda/closure – JavaScript、Python、Scala 到 Java SE 7lambda/closure – JavaScript、Python、Scala 到 Java SE 7
lambda/closure – JavaScript、Python、Scala 到 Java SE 7Justin Lin
 
Javascript Training
Javascript TrainingJavascript Training
Javascript Trainingbeijing.josh
 
C++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesC++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesPeien Luo
 
Python learn guide
Python learn guidePython learn guide
Python learn guiderobin yang
 
神州泰岳测试试题(笔试)
神州泰岳测试试题(笔试)神州泰岳测试试题(笔试)
神州泰岳测试试题(笔试)yiditushe
 
Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍dennis zhuang
 
Python入門:5大概念初心者必備
Python入門:5大概念初心者必備Python入門:5大概念初心者必備
Python入門:5大概念初心者必備Derek Lee
 
Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18Derek Lee
 
博大正方C语言试题V2.0
博大正方C语言试题V2.0博大正方C语言试题V2.0
博大正方C语言试题V2.0yiditushe
 

Similar to JCConf 2023 - 深入淺出 Java 21 功能 (20)

第三章 栈和队列
第三章 栈和队列第三章 栈和队列
第三章 栈和队列
 
Swift 程序语言介绍
Swift 程序语言介绍Swift 程序语言介绍
Swift 程序语言介绍
 
Standford 2015 iOS讀書會 week2: 1. Applying MVC 2. More Swift and Foundation Fra...
Standford 2015 iOS讀書會 week2: 1. Applying MVC 2. More Swift and Foundation Fra...Standford 2015 iOS讀書會 week2: 1. Applying MVC 2. More Swift and Foundation Fra...
Standford 2015 iOS讀書會 week2: 1. Applying MVC 2. More Swift and Foundation Fra...
 
第三章 栈和队列(新)
第三章 栈和队列(新)第三章 栈和队列(新)
第三章 栈和队列(新)
 
Arduino L2
Arduino L2Arduino L2
Arduino L2
 
C程式-函式與巨集
C程式-函式與巨集C程式-函式與巨集
C程式-函式與巨集
 
Python学习笔记
Python学习笔记Python学习笔记
Python学习笔记
 
Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版
 
Arduino程式快速入門
Arduino程式快速入門Arduino程式快速入門
Arduino程式快速入門
 
lambda/closure – JavaScript、Python、Scala 到 Java SE 7
lambda/closure – JavaScript、Python、Scala 到 Java SE 7lambda/closure – JavaScript、Python、Scala 到 Java SE 7
lambda/closure – JavaScript、Python、Scala 到 Java SE 7
 
Javascript Training
Javascript TrainingJavascript Training
Javascript Training
 
C++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesC++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New Features
 
Python learn guide
Python learn guidePython learn guide
Python learn guide
 
神州泰岳测试试题(笔试)
神州泰岳测试试题(笔试)神州泰岳测试试题(笔试)
神州泰岳测试试题(笔试)
 
算法基础报告
算法基础报告算法基础报告
算法基础报告
 
Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍Ihome inaction 篇外篇之fp介绍
Ihome inaction 篇外篇之fp介绍
 
Swift基礎
Swift基礎Swift基礎
Swift基礎
 
Python入門:5大概念初心者必備
Python入門:5大概念初心者必備Python入門:5大概念初心者必備
Python入門:5大概念初心者必備
 
Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18
 
博大正方C语言试题V2.0
博大正方C语言试题V2.0博大正方C语言试题V2.0
博大正方C语言试题V2.0
 

More from Joseph Kuo

JCConf 2022 - New Features in Java 18 & 19
JCConf 2022 - New Features in Java 18 & 19JCConf 2022 - New Features in Java 18 & 19
JCConf 2022 - New Features in Java 18 & 19Joseph Kuo
 
JCConf 2021 - Java17: The Next LTS
JCConf 2021 - Java17: The Next LTSJCConf 2021 - Java17: The Next LTS
JCConf 2021 - Java17: The Next LTSJoseph Kuo
 
JCConf 2020 - New Java Features Released in 2020
JCConf 2020 - New Java Features Released in 2020JCConf 2020 - New Java Features Released in 2020
JCConf 2020 - New Java Features Released in 2020Joseph Kuo
 
TWJUG x Oracle Groundbreakers 2019 Taiwan - What’s New in Last Java Versions
TWJUG x Oracle Groundbreakers 2019 Taiwan - What’s New in Last Java VersionsTWJUG x Oracle Groundbreakers 2019 Taiwan - What’s New in Last Java Versions
TWJUG x Oracle Groundbreakers 2019 Taiwan - What’s New in Last Java VersionsJoseph Kuo
 
JCConf 2018 - Retrospect and Prospect of Java
JCConf 2018 - Retrospect and Prospect of JavaJCConf 2018 - Retrospect and Prospect of Java
JCConf 2018 - Retrospect and Prospect of JavaJoseph Kuo
 
JCConf 2017 - Next Generation of Cloud Computing: Edge Computing and Apache E...
JCConf 2017 - Next Generation of Cloud Computing: Edge Computing and Apache E...JCConf 2017 - Next Generation of Cloud Computing: Edge Computing and Apache E...
JCConf 2017 - Next Generation of Cloud Computing: Edge Computing and Apache E...Joseph Kuo
 
JCConf 2016 - Cloud Computing Applications - Hazelcast, Spark and Ignite
JCConf 2016 - Cloud Computing Applications - Hazelcast, Spark and IgniteJCConf 2016 - Cloud Computing Applications - Hazelcast, Spark and Ignite
JCConf 2016 - Cloud Computing Applications - Hazelcast, Spark and IgniteJoseph Kuo
 
Establish The Core of Cloud Computing Application by Using Hazelcast (Chinese)
Establish The Core of  Cloud Computing Application  by Using Hazelcast (Chinese)Establish The Core of  Cloud Computing Application  by Using Hazelcast (Chinese)
Establish The Core of Cloud Computing Application by Using Hazelcast (Chinese)Joseph Kuo
 

More from Joseph Kuo (8)

JCConf 2022 - New Features in Java 18 & 19
JCConf 2022 - New Features in Java 18 & 19JCConf 2022 - New Features in Java 18 & 19
JCConf 2022 - New Features in Java 18 & 19
 
JCConf 2021 - Java17: The Next LTS
JCConf 2021 - Java17: The Next LTSJCConf 2021 - Java17: The Next LTS
JCConf 2021 - Java17: The Next LTS
 
JCConf 2020 - New Java Features Released in 2020
JCConf 2020 - New Java Features Released in 2020JCConf 2020 - New Java Features Released in 2020
JCConf 2020 - New Java Features Released in 2020
 
TWJUG x Oracle Groundbreakers 2019 Taiwan - What’s New in Last Java Versions
TWJUG x Oracle Groundbreakers 2019 Taiwan - What’s New in Last Java VersionsTWJUG x Oracle Groundbreakers 2019 Taiwan - What’s New in Last Java Versions
TWJUG x Oracle Groundbreakers 2019 Taiwan - What’s New in Last Java Versions
 
JCConf 2018 - Retrospect and Prospect of Java
JCConf 2018 - Retrospect and Prospect of JavaJCConf 2018 - Retrospect and Prospect of Java
JCConf 2018 - Retrospect and Prospect of Java
 
JCConf 2017 - Next Generation of Cloud Computing: Edge Computing and Apache E...
JCConf 2017 - Next Generation of Cloud Computing: Edge Computing and Apache E...JCConf 2017 - Next Generation of Cloud Computing: Edge Computing and Apache E...
JCConf 2017 - Next Generation of Cloud Computing: Edge Computing and Apache E...
 
JCConf 2016 - Cloud Computing Applications - Hazelcast, Spark and Ignite
JCConf 2016 - Cloud Computing Applications - Hazelcast, Spark and IgniteJCConf 2016 - Cloud Computing Applications - Hazelcast, Spark and Ignite
JCConf 2016 - Cloud Computing Applications - Hazelcast, Spark and Ignite
 
Establish The Core of Cloud Computing Application by Using Hazelcast (Chinese)
Establish The Core of  Cloud Computing Application  by Using Hazelcast (Chinese)Establish The Core of  Cloud Computing Application  by Using Hazelcast (Chinese)
Establish The Core of Cloud Computing Application by Using Hazelcast (Chinese)
 

JCConf 2023 - 深入淺出 Java 21 功能

  • 1. 深入淺出 Java 21 功能 Joseph Kuo (CyberJos) VP of Software Development @ NextDrive 如果有人能簡介 Java 21 功能的 話那該有多好!
  • 2. About Me • 1993 年學程式、1999 年 Java 1.2 • 主修應數、副修資工 • 資訊教育機構、加值簡訊服務、雲端影音平台、 全球電子商務、資訊安全防護、用戶行為管理、 智慧物聯網平台 • 講師經歷:JCConf、Oracle Groundbreakers、 AWS Summit、…… • 系統架構、雲端運算、邊緣運算、後端技術、軟 工設計、智慧物聯 • 希望能一輩子玩技術寫程式到老 2 https://cyberjos.blog/
  • 3. Taiwan Java User Group • FB:https://www.facebook.com/groups/twjug • 官方網站:http://www.twjug.org/ • LINE 社群「Java 程式語言討論區」 3
  • 4. 歡迎來到 JCConf 2023 4 Virtual Threads Spring Framework Kotlin JVM Jakarta EE GraalVM Scala Android Redis MySQL VMware Lombok Javalin AWS Azure LINE Autopass Java 21 LTS SoftLeader Oracle
  • 5. 2023 State of Java Ecosystem
  • 6. 2023 State of Java Ecosystem • New Relic first published in March 2020 • 2023 state was published in April 2023 • Based on data gathered from millions of applications providing performance data • The data for this report was drawn entirely from applications reporting to New Relic in January 2023 and does not provide a global picture of Java usage. 6 Source: https://newrelic.com/resources/report/2023-state-of-java-ecosystem
  • 7. LTS 中 Java 17 用戶增長率顯著 7
  • 8. 非 LTS 版本仍以 14 穩居第一 8
  • 9. Amazon 躍升 JDK 供應商龍頭 9
  • 10. G1GC 維持 Java 11 後領先地位 10
  • 11. Java 21 有哪些新功能? 11 聽說 Java 21 是下個 LTS 版 本,它有哪些新功能呢? 我們需要開始使用它嗎? 好,我們完成系統轉換到 Java 11 平台,甚至有一些 舊服務以及服務已經採用 Java 17 開發了……
  • 13. 早在 Java 遠古時代 (< Java 7) String text = "NULL"; if (obj instanceof String) { // check String s = (String) obj; // assign + cast text = "String: " + s; } else if (obj instanceof Integer) { Integer i = (Integer) obj; if (i < 10000) text = "Small Int: " + i; else text = "Big Int: " + i; } else if (obj instanceof Long) { Long l = (Long) obj; text = "Long: " + l; } else if (obj instanceof Number) { Number n = (Number) obj; text = "Number: " + n; } else if (obj != null) text = "Object: " + obj; 13
  • 14. Java 16 加強了 instanceof String text = "NULL"; if (obj instanceof String s) { // check + assign + cast text = "String: " + s; } else if (obj instanceof Integer i && i < 10_000) { text = "Small Int: " + i; } else if (obj instanceof Integer i) { text = "Big Int: " + i; } else if (obj instanceof Long l) { text = "Long: " + l; } else if (obj instanceof Number n) { text = "Number: " + n; } else if (obj != null) { text = "Object: " + obj; } 14 雖然簡化了,但還 是有很多判斷式!
  • 16. Pattern Matching for switch String text = switch (obj) { case null -> "NULL"; // null check case String s -> "String: " + s; case Integer i when i < 10_000 -> "Small Int: " + i; case Integer i -> "Big Int: " + i; case Long l -> "Long: " + l; case Number n -> "Number: " + n; default -> "Object: " + obj; } 16 1. 可以檢查 null 2. 可以用 when 來增加判斷式 (guarded pattern) 3. Java14 讓 switch 可以回 傳值並設定 text 變數內容
  • 17. 注意!上下順序很重要! String text = switch (obj) { case null -> "NULL"; case String s -> "String: " + s; case Number n -> "Number: " + n; // Error: this case label is dominated // by a preceding case label case Integer i -> "Integer: " + i; // unreachable case Long l -> "Long: " + l; default -> "Object: " + obj; } 17 到不了的地方叫遠方
  • 18. 支配權 Dominance • 父類別大於子類別 (例:Number > Integer) • Unguarded pattern 大於 guarded pattern (case Type > case Type when expr.) • Guarded pattern when true 大於其他 pattern(guarded & unguarded) • Pattern 大於常數(Integer i > 1, 2) • Enum 類別大於常數(Color > Color.RED) • when 不會進行過多的額外檢查 18
  • 19. 建議撰寫原則 • 先寫 case null (純粹為了易讀性與維護性) • 次寫 case 常數和列舉常數 • 再寫 guarded 的 case Type t when 判斷式 • 後寫 unguarded 的 case Type t • 特別注意 Type 的子類別要放在父類別之前 • 最後寫 default 做收尾(若未窮舉的話) 19
  • 20. 搭配 enum 一起用 enum Color { RED, GREEN, BLUE, YELLOW, PURPLE } String text = switch (obj) { case null -> "NULL"; case Color.BLUE -> "Color.BLUE"; // 短版本 case Color c when c == Color.RED -> "Color.RED";// 與上式等價 case Color c -> "Color: others"; // 注意支配性 case int[] ia -> "Int array: " + ia; // 陣列也可以 default -> "Object: " + obj; } 20 記得 enum、sealed 需要注 意是否要窮舉所有可能值!
  • 22. class Point { // immutable private final int x, y; public Point(int x, int y) { this.x=x; this.y=y; } public int x() { return x; } public int y() { return y; } public boolean equals(Object o) { return o instanceof Point && ((Point) o).x == x && ((Point) o).y == y; } public int hashCode() { return Objects.hash(x, y); } public String toString() { return String.format("Point[x=%d, y=%d]", x, y); } } 回到 Java 上古時代(= Java 7) 22
  • 23. Java16 record + Java17 sealed 23 record Point(int x, int y) {} // 上頁的程式碼濃縮成一行了 // 順便增加一些複雜度 sealed interface Shape permits Circle, Rectangle, Triangle { public static void check(Point p) { if (p.x() <= 0) throw new IllegalArgumentException("NG!"); } } record Circle(Point p, int r, Color c) implements Shape {} record Rectangle(Point p1, Point p2, Color c) implements Shape {} record Triangle(Point p1, Point p2, Point p3, Color c) implements Shape {}
  • 24. return switch (obj) { case null -> "NULL"; case Point p -> "Point: " + p; // Pattern matching case Circle(Point p, int r, Color c) // record pattern -> "Circle: p:" + p; case Rectangle(Point p1, Point p2, Color c) -> "Rectangle: 1:" + p1 + ", 2:" + p2; case Triangle(Point p1, Point p2, Point p3, Color c) -> "Triangle: 1:" + p1 + ", 2:" + p2 + ", 3:" + p3; default -> "Object: " + obj; }; 24 這樣一來就不需要使用 case Circle c -> c.p(); Record Pattern
  • 25. record Pair<T>(T a, T b) {} private Pair<Shape> o; switch (o) { // 要窮舉所有可能值 case Pair<Shape>(Circle c, Shape s) -> ... case Pair<Shape>(Rectangle r, Circle c) -> ... case Pair<Shape>(Rectangle r, Rectangle r) -> ... case Pair<Shape>(Rectangle r, Triangle t) -> ... case Pair<Shape>(Triangle t, Shape s) -> ... // 或是用下列任一行收尾 case Pair<Shape>(var a, var b) -> ... case Pair<Shape> x -> ... default -> ... } 25 要注意是否已窮舉
  • 26. If record in if // Java 16 ~ 20 if (o instanceof Point p) { int x = p.x(); int y = p.y(); System.out.println(x + y); } // Java 21 Record pattern if (o instanceof Point(int x, int y)) { System.out.println(x + y); } 26
  • 27. 更複雜的 Record Pattern if (o instanceof Circle(Point(int x, int y), int r, Color c)) { System.out.println(x + y); } if (o instanceof Rectangle(Point(var x,var y), var p2, var c)) { System.out.println(x); // 也可以使用 var } if (o instanceof Triangle(Point(int x1, int y1), Point(var x2, var y2), Point p3, var c)) { System.out.println(c); } 27 程式碼能夠 簡化嗎?
  • 28. Unnamed Patterns (Preview) if (o instanceof Circle(Point(int x, int y), int _, Color _) { System.out.println(x + y); // unnamed pattern variable } if (o instanceof Rectangle(Point(var x, _), _, _) { System.out.println(x); // unnamed pattern (Type v) } if (o instanceof Triangle(_, _, _, var c) { // unnamed pattern System.out.println(c); } if (o instanceof Point _) { // unnamed pattern variable System.out.println("Point!"); } 28 程式碼複雜度降 低,可讀性增加!
  • 29. Unnamed Variables (Preview) var points = new ArrayList<Point>(List.of(p1, p2, p3, p4, p5)); for (Point _ : points) { // enhanced for loop if (DEBUG) count++; } for (int i = 0, _ = init(); i < count; i++) { // for statement try (var _ = getRes()) { // try-with-resources statement Point p = points.getFirst(); Shape.check(p); m.putAll(Arrays.stream(Color.values()) .map(c -> new Circle(p, i, c)) .collect(toMap(Function.identity(), _ -> 1L)) //lambda ); } catch (IllegalArgumentException _) { // exception var _ = points.removeFirst(); // local variable } } 29
  • 30. 這是禁止事項 🙅 • o instanceof _ • o instanceof _(int x, int y) • o instanceof var _ • case _ • case var _ • case Type(_) • void test(String x, int _) {} • ... 30
  • 32. 各自表述,沒有共識 First element Last element List list.get(0) list.get(list.size() - 1) Deque deque.getFirst() deque.getLast() SortedSet set.first() set.last() LinkedHashSet set.iterator().next() // missing 32 不同集合取得 首尾元素有不 同做法……
  • 33. Sequenced Collections • 增加 Collections 的學習曲線和複雜度(X) • 加強集合元素的序列處理能力(O) • 引入新的介面和方法來簡化有序集合 • 提供全員一致的方法來存取首尾元素 • 同時提供反序集合的生成方法 33
  • 35. SequencedCollection // new method SequencedCollection<E> reversed(); // methods promoted from Deque void addFirst(E); void addLast(E); E getFirst(); E getLast(); E removeFirst(); E removeLast(); // add*(E) & remove*() are optional (unmodifiable) // get*() & remove*() throw NoSuchElementEx if empty 35
  • 36. SequencedSet // covariant override SequencedSet<E> reversed(); // add*(E) throw UnsupportedOperationEx in SortedSet 36 臣妾做不到啊……
  • 37. SequencedMap // new method SequencedMap<K, V> reversed(); SequencedSet<K> sequencedKeySet(); SequencedCollection<V> sequencedValues(); SequencedSet<Entry<K, V>> sequencedEntrySet(); V putFirst(K, V); V putLast(K, V); // put*(K, V) and poll*(K, V) are optional // put*(K, V) throw UnsupportedOperationEx in SortedMap // methods promoted from NavigableMap Entry<K, V> firstEntry(); Entry<K, V> lastEntry(); Entry<K, V> pollFirstEntry(); Entry<K, V> pollLastEntry(); 37
  • 38. Virtual Threads • 繼承 Thread 的輕量級執行緒,可大幅減少撰 寫、維護和監控高吞吐並行程式的工作量 • 用 thread-per-request 風格撰寫的服務器應用 程式能以接近最佳化的硬體使用率來進行擴容 • 原本使用 Thread API 的既有程式碼能在最小的 改動下採用虛擬執行緒 • 與既有的 JDK 工具整合,使其易於使用、除錯 和量測 38
  • 39. Thread-per-request 風格 • 服務器程式利用執行緒去完全處理每個請求 • Little's Law(L=λW):給定的請求處理時間 內(延遲λ),服務器同時能處理的請求數(並 行L )與抵達率(吞吐W)成比例增長 • 綜合上述,增加吞吐量代表執行緒數量也要增 加,才能在維持相同的延遲 • 但執行緒的數量有其限制,因為 JDK 的平台執 行緒對應的是代價昂貴的 OS 執行緒 39 另外有非 同步風格
  • 40. 改進 thread-per-request 效率 • 為了在擴展應用程式的同時能與平台維持協作, 所以用更有效率的方式來實作執行緒 • 如同 OS 將大量虛擬定址空間映射到有限實體 RAM 一樣,JVM 將大量虛擬執行緒映射到少 量的 OS 執行緒,達成增加執行緒數量 • 虛擬執行緒會被臨時對映到載體執行緒中,一 旦遇到阻塞操作,就會從載體中移除,而載體 可以執行另一個虛擬執行緒(新的或之前被阻 塞的) 40
  • 41. 虛擬執行緒要怎麼用? try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10_000) .forEach(i -> { executor.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); return i; }); }); } 1. 無負擔建立虛擬執行緒 2. 可以設定較大的數量 3. 背後只用到少量OS執行緒 4. 如果用平台執行緒的話會出事 41
  • 42. 你也許感興趣的事情 • 虛擬執行緒不會加快執行速度,事實上它與平 台執行緒的速度一致。它的目的是為了提供更 高的吞吐量,而不是更低的延遲 • 適用於並行數量多且非 CPU 負載時(I/O) • 不要用執行緒池,因為它便宜且大量,主打DB 查詢或 HTTP 請求。平台執行緒很昂貴,所以 才需要執行緒池保持其長壽並方便調度 • 小心 ThreadLocal,建議用 ScopedValue • Structured Concurrency 預設使用虛擬執行緒 42
  • 43. public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } 回到 Java 盤古開天時代說哈囉 1. 程式碼太長 2. 需解釋太多觀念 3. 太多不需要的結構 4. 有點太過雜亂 5. 對新手不友善 43
  • 44. class HelloWorld { void main() { System.out.println("Hello, World!"); } } Instance Main Methods(Preview) 1. main 方法不需要是 static 2. 也不需要 public 3. 不需要有 String[] 傳入值 44
  • 45. 1. 類別中非 private 的 static void main(String[] args) 2. 類別中非 private 的 static void main() 3. 類別中本身宣告或繼承自父類別的 非 private 的 void main(String[] args) 4. 類別中本身宣告或繼承自父類別的 非 private 的 void main() 如何挑選? 45
  • 46. void main() { System.out.println("Hello, World!"); } // T.java -> final class T extends Object implements Unnamed Classes (Preview) 1. 讓 class 宣告變成可有可無 2. 當方法和欄位沒有被類別宣告包覆 時,會自動將其視為未命名類的成員 3. 不能被其他類別引用,故需要 main(), 也無法外部呼叫靜態成員與建構子 4. 不能實作介面 46
  • 47. private String hello = "Hello"; String name() { return "World"; } void main() { System.out.println(hello + ", " + name() + "!"); System.out.println(STR."{hello}, {name()}!"); } 未命名類別內可以網內互打 了解!不過 STR 和 {} 是什麼東西? 47
  • 48. String Templates (Preview) // STR 是自動import到所有原始檔的 public static final 欄位 int x = 10, y = 20; STR."{x} + {y} = {x + y}"; // 10 + 20 = 30 // 可呼叫方法和存取欄位 STR."{req.getTime()} from {req.ipAddress}"; // 雙引號不需要跳脫字元 STR."{file} {file.exists() ? "does" : "doesn't"} exist"; // 可放入各式運算式,包括 STR 本身,變成套嵌俄羅斯娃娃 STR."{array[index++]}, {STR."{array[index++]}"}"; // 當然,也支援 String Block STR.""" { "name": "{record.name}", "phone": "{record.phone}" """; 48
  • 50. Core Java Library • JEP 431: Sequenced Collections • JEP 442: Foreign Function & Memory API (Third Preview) • JEP 444: Virtual Threads • JEP 446: Scoped Values (Preview) • JEP 448: Vector API (Sixth Incubator) • JEP 453: Structured Concurrency (Preview) 50
  • 51. Java Language Specification • JEP 430: String Templates (Preview) • JEP 440: Record Patterns • JEP 441: Pattern Matching for switch • JEP 443: Unnamed Patterns and Variables (Preview) • JEP 445: Unnamed Classes and Instance Main Methods (Preview) 51
  • 52. HotSpot & Security • JEP 439: Generational ZGC • JEP 449: Deprecate the Windows 32-bit x86 Port for Removal • JEP 451: Prepare to Disallow the Dynamic Loading of Agents • JEP 452: Key Encapsulation Mechanism API • NOTE THAT JEP 404, Generational Shenandoah (Experimental), originally targeted for 21, was officially removed 52