JEP
JEP 360: Sealed Classes (Preview)
内容
簡単に言うと、継承先を限定することができるクラスやインタフェースを作れるらしい。
これができる背景には、目的にそぐわない継承やインタフェースの実装が乱立していた事実があるのかもしれない。※個人の予想です。
コード的には、下記のような記載になる。
sealed class Shape permits Circle, Rectangle, Square { }
目新しい違いは、sealedとpermits。
sealedで、今回の機能を使うことを宣言している。
そして、継承を許可するクラスは、permitsで宣言。
例の内容だと、Circle, Rectangle, Square
が継承を許可されたことになる。
調査
まずは、載ってるサンプルを試す。
sealed class Shape permits Circle, Rectangle, Square { }
class Circle extends Shape { }
class Rectangle extends Shape { }
class Square extends Shape { }
javaファイル作ったので、コンパイル。
preview版なので、--enable-preview
をつける。
※本当はコンパイルしようとしたら指摘されて気づいた。。。
$ javac --enable-preview -source 15 JEP360.java
JEP360.java:3: エラー: sealed、non-sealedまたはfinal修飾子が必要です
class Circle extends Shape { }
^
JEP360.java:4: エラー: sealed、non-sealedまたはfinal修飾子が必要です
class Rectangle extends Shape { }
^
JEP360.java:5: エラー: sealed、non-sealedまたはfinal修飾子が必要です
class Square extends Shape { }
なんか、final句つけないとダメらしい。。。
つまり、1世代しか生き残れない親クラスってわけか。
継承できる先を指定しているので、たしかに、そうだなって、思った。
final句つけて、再コンパイル
sealed class Shape permits Circle, Rectangle, Square { }
final class Circle extends Shape { }
final class Rectangle extends Shape { }
final class Square extends Shape { }
$ javac --enable-preview -source 15 JEP360.java
通った。。。
でも、動作を確認できないから、動作確認ようのクラスを追加しないとダメだね。。。
とりあえず作ったサンプルコード!
sealed class Shape permits Circle, Rectangle, Square { }
final class Circle extends Shape {
public int center() {
return 0;
}
}
final class Rectangle extends Shape {
public int length() {
return 1;
}
}
final class Square extends Shape {
public int side() {
return 2;
}
}
public class JEP360 {
public static void main(String[] args) {
Shape s = new Circle();
System.out.println(JEP360.getCenter(s));
}
public static int getCenter(Shape shape) {
if (shape instanceof Circle c) {
return c.center();
} else if (shape instanceof Rectangle r) {
return r.length();
} else if (shape instanceof Square s) {
return s.side();
}
return -1;
}
}
そんでもって実行。
期待値としては、Circleクラスを渡しているので、0が返ってきて欲しい。
$ javac --enable-preview -source 15 JEP360.java
$ java --enable-preview JEP360
0
ちゃんと返ってきたね。
指定以外のクラスで継承するとどうなるのか?
とりあえずサンプルコード
sealed class Shape permits Circle, Rectangle, Square { }
final class Circle extends Shape {
public int center() {
return 0;
}
}
final class Rectangle extends Shape {
public int length() {
return 1;
}
}
final class Square extends Shape {
public int side() {
return 2;
}
}
final class Test extends Shape {
}
あらたにTest
というクラスを追加した。
このクラスは、Shapeを継承しているけど、Shape側のpermitsには含まれていない。
たぶん、コンパイル時にエラーで弾かれると思う。
$ javac --enable-preview -source 15 JEP360.java
JEP360.java:19: エラー: クラスはシール・クラスShapeを拡張できません
final class Test extends Shape {
^
当たり!!
やっぱり、コンパイル時に弾かれるよね。
正解したから、何かご褒美が欲しい。
permitsに存在しないクラスを指定するとどうなるのか?
とりあえず、サンプルコード
sealed class Shape permits Circle, Rectangle, Square, Unknown { }
final class Circle extends Shape {
public int center() {
return 0;
}
}
final class Rectangle extends Shape {
public int length() {
return 1;
}
}
final class Square extends Shape {
public int side() {
return 2;
}
}
Unknown が存在しないクラス。
その他は定義済み。
期待値としては、そのままビルド通っていいのでは?って思ってる。
$ javac --enable-preview -source 15 JEP360.java
JEP360.java:1: エラー: シンボルを見つけられません
sealed class Shape permits Circle, Rectangle, Square, Unknown { }
^
シンボル: クラス Unknown
JEP360.java:1: エラー: 無効なpermits句
sealed class Shape permits Circle, Rectangle, Square, Unknown { }
^
(スーパータイプUnknownへの参照が不正です)
なんだと。。。
ビルド時にクラスの存在も確認しているのか。。。
感想
クラスをグルーピングできるようになるって理解でよいだろうか?
使う側で不用意な拡張とかされないようになるから、FW側を安全に使うようになる理解でいるが、どうだろう?
若干、疑問に思っているのは、オープン・クローズドの原則と相反する気がするのだが、気のせいだろうか?
なぜ入ったのか、経緯をよく理解できてないせいか、どうにも腑に落ちない。
今年はイベントがほとんどないから、理解があっているのか確かめる場が無いのが痛い。。。