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側を安全に使うようになる理解でいるが、どうだろう?
若干、疑問に思っているのは、オープン・クローズドの原則と相反する気がするのだが、気のせいだろうか?
なぜ入ったのか、経緯をよく理解できてないせいか、どうにも腑に落ちない。
今年はイベントがほとんどないから、理解があっているのか確かめる場が無いのが痛い。。。