Add sealed module: modelling domains with sealed types and exhaustive switch
Payment model, permits and the three modifiers, default-branch trap, generic and recursive hierarchies, visitor comparison, run-time enforcement, modules. Transcripts from JDK 21, 25 and 27. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01TF9JWFvJSNm6HVzswzZU5a
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
$ javac src/PaymentDemo.java && java PaymentDemo (21.0.10+7-Ubuntu-124.04)
|
||||
Card[last4=4242, amount=500] -> fee 10 | card payment ending 4242
|
||||
Card[last4=1111, amount=25000] -> fee 500 | large card payment ending 1111
|
||||
Upi[vpa=ankur@bank, amount=500] -> fee 0 | UPI payment from ankur@bank
|
||||
NetBanking[bank=HDFC, amount=500] -> fee 15 | net banking via HDFC
|
||||
|
||||
$ javac src/PaymentDemo.java && java PaymentDemo (25.0.4.1+1-LTS)
|
||||
Card[last4=4242, amount=500] -> fee 10 | card payment ending 4242
|
||||
Card[last4=1111, amount=25000] -> fee 500 | large card payment ending 1111
|
||||
Upi[vpa=ankur@bank, amount=500] -> fee 0 | UPI payment from ankur@bank
|
||||
NetBanking[bank=HDFC, amount=500] -> fee 15 | net banking via HDFC
|
||||
|
||||
$ javac src/PaymentDemo.java && java PaymentDemo (27+35)
|
||||
Card[last4=4242, amount=500] -> fee 10 | card payment ending 4242
|
||||
Card[last4=1111, amount=25000] -> fee 500 | large card payment ending 1111
|
||||
Upi[vpa=ankur@bank, amount=500] -> fee 0 | UPI payment from ankur@bank
|
||||
NetBanking[bank=HDFC, amount=500] -> fee 15 | net banking via HDFC
|
||||
@@ -0,0 +1,44 @@
|
||||
$ javac src/Reflect.java && java Reflect (21.0.10+7-Ubuntu-124.04)
|
||||
Shape: sealed, permits [Circle, Poly, Custom]
|
||||
Circle: final
|
||||
Poly: sealed, permits [Tri, Quad]
|
||||
Tri: final
|
||||
Quad: final
|
||||
Custom: open (non-sealed or plain)
|
||||
Hexagon: open (non-sealed or plain)
|
||||
circle triangle quad custom:Hexagon
|
||||
|
||||
$ javac src/Reflect.java && java Reflect (25.0.4.1+1-LTS)
|
||||
Shape: sealed, permits [Circle, Poly, Custom]
|
||||
Circle: final
|
||||
Poly: sealed, permits [Tri, Quad]
|
||||
Tri: final
|
||||
Quad: final
|
||||
Custom: open (non-sealed or plain)
|
||||
Hexagon: open (non-sealed or plain)
|
||||
circle triangle quad custom:Hexagon
|
||||
|
||||
$ javac src/Reflect.java && java Reflect (27+35)
|
||||
Shape: sealed, permits [Circle, Poly, Custom]
|
||||
Circle: final
|
||||
Poly: sealed, permits [Tri, Quad]
|
||||
Tri: final
|
||||
Quad: final
|
||||
Custom: open (non-sealed or plain)
|
||||
Hexagon: open (non-sealed or plain)
|
||||
circle triangle quad custom:Hexagon
|
||||
|
||||
$ javac src/ImplicitPermits.java && java ImplicitPermits (21.0.10+7-Ubuntu-124.04)
|
||||
[ImplicitPermits$A, ImplicitPermits$B]
|
||||
|
||||
$ javac src/ImplicitPermits.java && java ImplicitPermits (25.0.4.1+1-LTS)
|
||||
[ImplicitPermits$A, ImplicitPermits$B]
|
||||
|
||||
$ javac src/ImplicitPermits.java && java ImplicitPermits (27+35)
|
||||
[ImplicitPermits$A, ImplicitPermits$B]
|
||||
|
||||
$ javap -v -cp . 'PaymentDemo$Payment' | grep -A3 PermittedSubclasses (25.0.4.1+1-LTS)
|
||||
PermittedSubclasses:
|
||||
PaymentDemo$Card
|
||||
PaymentDemo$Upi
|
||||
PaymentDemo$NetBanking
|
||||
@@ -0,0 +1,130 @@
|
||||
$ javac broken/WalletNotHandled.java (21.0.10+7-Ubuntu-124.04)
|
||||
broken/WalletNotHandled.java:10: error: the switch expression does not cover all possible input values
|
||||
return switch (p) {
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/WalletNotHandled.java (25.0.4.1+1-LTS)
|
||||
broken/WalletNotHandled.java:10: error: the switch expression does not cover all possible input values
|
||||
return switch (p) {
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/WalletNotHandled.java (27+35)
|
||||
broken/WalletNotHandled.java:10: error: the switch expression does not cover all possible input values
|
||||
return switch (p) {
|
||||
^
|
||||
missing patterns:
|
||||
Wallet _
|
||||
1 error
|
||||
|
||||
$ javac broken/NotPermitted.java (21.0.10+7-Ubuntu-124.04)
|
||||
broken/NotPermitted.java:5: error: class is not allowed to extend sealed class: Payment (as it is not listed in its 'permits' clause)
|
||||
record Cheque(long amount) implements Payment {} // not in the permits clause
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/NotPermitted.java (25.0.4.1+1-LTS)
|
||||
broken/NotPermitted.java:5: error: class is not allowed to extend sealed class: Payment (as it is not listed in its 'permits' clause)
|
||||
record Cheque(long amount) implements Payment {} // not in the permits clause
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/NotPermitted.java (27+35)
|
||||
broken/NotPermitted.java:5: error: class is not allowed to extend sealed class: Payment (as it is not listed in its 'permits' clause)
|
||||
record Cheque(long amount) implements Payment {} // not in the permits clause
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/MissingModifier.java (21.0.10+7-Ubuntu-124.04)
|
||||
broken/MissingModifier.java:3: error: sealed, non-sealed or final modifiers expected
|
||||
class Card implements Payment {} // must say final, sealed or non-sealed
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/MissingModifier.java (25.0.4.1+1-LTS)
|
||||
broken/MissingModifier.java:3: error: sealed, non-sealed or final modifiers expected
|
||||
class Card implements Payment {} // must say final, sealed or non-sealed
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/MissingModifier.java (27+35)
|
||||
broken/MissingModifier.java:3: error: sealed, non-sealed or final modifiers expected
|
||||
class Card implements Payment {} // must say final, sealed or non-sealed
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/NoSubclass.java (21.0.10+7-Ubuntu-124.04)
|
||||
broken/NoSubclass.java:2: error: sealed class must have subclasses
|
||||
sealed interface Payment {} // no permits clause and no subtype in this file
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/NoSubclass.java (25.0.4.1+1-LTS)
|
||||
broken/NoSubclass.java:2: error: sealed class must have subclasses
|
||||
sealed interface Payment {} // no permits clause and no subtype in this file
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/NoSubclass.java (27+35)
|
||||
broken/NoSubclass.java:2: error: sealed class must have subclasses
|
||||
sealed interface Payment {} // no permits clause and no subtype in this file
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/NonSealedWrongPlace.java (21.0.10+7-Ubuntu-124.04)
|
||||
broken/NonSealedWrongPlace.java:3: error: non-sealed modifier not allowed here
|
||||
non-sealed class Oops implements Plain {} // non-sealed only makes sense under a sealed supertype
|
||||
^
|
||||
(class NonSealedWrongPlace.Oops does not have any sealed supertypes)
|
||||
1 error
|
||||
|
||||
$ javac broken/NonSealedWrongPlace.java (25.0.4.1+1-LTS)
|
||||
broken/NonSealedWrongPlace.java:3: error: non-sealed modifier not allowed here
|
||||
non-sealed class Oops implements Plain {} // non-sealed only makes sense under a sealed supertype
|
||||
^
|
||||
(class NonSealedWrongPlace.Oops does not have any sealed supertypes)
|
||||
1 error
|
||||
|
||||
$ javac broken/NonSealedWrongPlace.java (27+35)
|
||||
broken/NonSealedWrongPlace.java:3: error: non-sealed modifier not allowed here
|
||||
non-sealed class Oops implements Plain {} // non-sealed only makes sense under a sealed supertype
|
||||
^
|
||||
(class NonSealedWrongPlace.Oops does not have any sealed supertypes)
|
||||
1 error
|
||||
|
||||
$ javac broken/AnonymousSealed.java (21.0.10+7-Ubuntu-124.04)
|
||||
broken/AnonymousSealed.java:5: error: anonymous classes must not extend sealed classes
|
||||
return new Payment() {}; // an anonymous class is a subtype nobody listed
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/AnonymousSealed.java (25.0.4.1+1-LTS)
|
||||
broken/AnonymousSealed.java:5: error: anonymous classes must not extend sealed classes
|
||||
return new Payment() {}; // an anonymous class is a subtype nobody listed
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/AnonymousSealed.java (27+35)
|
||||
broken/AnonymousSealed.java:5: error: anonymous classes must not extend sealed classes
|
||||
return new Payment() {}; // an anonymous class is a subtype nobody listed
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/xpkg/a/Base.java broken/xpkg/b/Impl.java (21.0.10+7-Ubuntu-124.04)
|
||||
broken/xpkg/a/Base.java:2: error: class Base in unnamed module cannot extend a sealed class in a different package
|
||||
public sealed interface Base permits b.Impl {}
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/xpkg/a/Base.java broken/xpkg/b/Impl.java (25.0.4.1+1-LTS)
|
||||
broken/xpkg/a/Base.java:2: error: class Base in unnamed module cannot extend a sealed class in a different package
|
||||
public sealed interface Base permits b.Impl {}
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/xpkg/a/Base.java broken/xpkg/b/Impl.java (27+35)
|
||||
broken/xpkg/a/Base.java:2: error: class Base in unnamed module cannot extend a sealed class in a different package
|
||||
public sealed interface Base permits b.Impl {}
|
||||
^
|
||||
1 error
|
||||
@@ -0,0 +1,8 @@
|
||||
$ javac src/WithDefault.java && java WithDefault (21.0.10+7-Ubuntu-124.04)
|
||||
fee for Wallet(1000) = 15 (the rule for Wallet is 1 percent, so 10; it compiled without any complaint)
|
||||
|
||||
$ javac src/WithDefault.java && java WithDefault (25.0.4.1+1-LTS)
|
||||
fee for Wallet(1000) = 15 (the rule for Wallet is 1 percent, so 10; it compiled without any complaint)
|
||||
|
||||
$ javac src/WithDefault.java && java WithDefault (27+35)
|
||||
fee for Wallet(1000) = 15 (the rule for Wallet is 1 percent, so 10; it compiled without any complaint)
|
||||
@@ -0,0 +1,26 @@
|
||||
$ javac src/ResultDemo.java && java ResultDemo (21.0.10+7-Ubuntu-124.04)
|
||||
21 -> ok 42
|
||||
abc -> error: not a number: abc
|
||||
|
||||
$ javac src/ResultDemo.java && java ResultDemo (25.0.4.1+1-LTS)
|
||||
21 -> ok 42
|
||||
abc -> error: not a number: abc
|
||||
|
||||
$ javac src/ResultDemo.java && java ResultDemo (27+35)
|
||||
21 -> ok 42
|
||||
abc -> error: not a number: abc
|
||||
|
||||
$ javac src/ExprDemo.java && java ExprDemo (21.0.10+7-Ubuntu-124.04)
|
||||
Add[left=Mul[left=Num[value=1], right=Num[value=7]], right=Neg[inner=Neg[inner=Num[value=3]]]]
|
||||
eval = 10
|
||||
simplify = Add[left=Num[value=7], right=Num[value=3]] (eval 10)
|
||||
|
||||
$ javac src/ExprDemo.java && java ExprDemo (25.0.4.1+1-LTS)
|
||||
Add[left=Mul[left=Num[value=1], right=Num[value=7]], right=Neg[inner=Neg[inner=Num[value=3]]]]
|
||||
eval = 10
|
||||
simplify = Add[left=Num[value=7], right=Num[value=3]] (eval 10)
|
||||
|
||||
$ javac src/ExprDemo.java && java ExprDemo (27+35)
|
||||
Add[left=Mul[left=Num[value=1], right=Num[value=7]], right=Neg[inner=Neg[inner=Num[value=3]]]]
|
||||
eval = 10
|
||||
simplify = Add[left=Num[value=7], right=Num[value=3]] (eval 10)
|
||||
@@ -0,0 +1,41 @@
|
||||
$ javac src/VisitorVersion.java && java VisitorVersion (21.0.10+7-Ubuntu-124.04)
|
||||
Card[last4=4242, amount=500] -> fee 10
|
||||
Upi[vpa=ankur@bank, amount=500] -> fee 0
|
||||
NetBanking[bank=HDFC, amount=500] -> fee 15
|
||||
|
||||
$ javac src/VisitorVersion.java && java VisitorVersion (25.0.4.1+1-LTS)
|
||||
Card[last4=4242, amount=500] -> fee 10
|
||||
Upi[vpa=ankur@bank, amount=500] -> fee 0
|
||||
NetBanking[bank=HDFC, amount=500] -> fee 15
|
||||
|
||||
$ javac src/VisitorVersion.java && java VisitorVersion (27+35)
|
||||
Card[last4=4242, amount=500] -> fee 10
|
||||
Upi[vpa=ankur@bank, amount=500] -> fee 0
|
||||
NetBanking[bank=HDFC, amount=500] -> fee 15
|
||||
|
||||
$ javac broken/VisitorAdded.java (21.0.10+7-Ubuntu-124.04)
|
||||
broken/VisitorAdded.java:9: error: Fee is not abstract and does not override abstract method visitWallet(Wallet) in Visitor
|
||||
static class Fee implements Visitor<Long> {
|
||||
^
|
||||
broken/VisitorAdded.java:13: error: Label is not abstract and does not override abstract method visitWallet(Wallet) in Visitor
|
||||
static class Label implements Visitor<String> {
|
||||
^
|
||||
2 errors
|
||||
|
||||
$ javac broken/VisitorAdded.java (25.0.4.1+1-LTS)
|
||||
broken/VisitorAdded.java:9: error: Fee is not abstract and does not override abstract method visitWallet(Wallet) in Visitor
|
||||
static class Fee implements Visitor<Long> {
|
||||
^
|
||||
broken/VisitorAdded.java:13: error: Label is not abstract and does not override abstract method visitWallet(Wallet) in Visitor
|
||||
static class Label implements Visitor<String> {
|
||||
^
|
||||
2 errors
|
||||
|
||||
$ javac broken/VisitorAdded.java (27+35)
|
||||
broken/VisitorAdded.java:9: error: Fee is not abstract and does not override abstract method visitWallet(Wallet) in Visitor
|
||||
static class Fee implements Visitor<Long> {
|
||||
^
|
||||
broken/VisitorAdded.java:13: error: Label is not abstract and does not override abstract method visitWallet(Wallet) in Visitor
|
||||
static class Label implements Visitor<String> {
|
||||
^
|
||||
2 errors
|
||||
@@ -0,0 +1,14 @@
|
||||
$ java -cp v1+Hack.class Main (Hack was compiled against a Payment that permitted it) (21.0.10+7-Ubuntu-124.04)
|
||||
loading Card: Card
|
||||
loading Hack...
|
||||
Exception in thread "main" java.lang.IncompatibleClassChangeError: class Hack cannot implement sealed interface Payment
|
||||
|
||||
$ java -cp v1+Hack.class Main (Hack was compiled against a Payment that permitted it) (25.0.4.1+1-LTS)
|
||||
loading Card: Card
|
||||
loading Hack...
|
||||
Exception in thread "main" java.lang.IncompatibleClassChangeError: Failed listed permitted subclass check: class Hack is not a permitted subclass of Payment
|
||||
|
||||
$ java -cp v1+Hack.class Main (Hack was compiled against a Payment that permitted it) (27+35)
|
||||
loading Card: Card
|
||||
loading Hack...
|
||||
Exception in thread "main" java.lang.IncompatibleClassChangeError: Failed listed permitted subclass check: class Hack is not a permitted subclass of Payment
|
||||
@@ -0,0 +1,8 @@
|
||||
$ javac -d out $(find multi/demo.pay -name '*.java') && java -p out -m demo.pay/pay.app.Main (21.0.10+7-Ubuntu-124.04)
|
||||
card in module demo.pay
|
||||
|
||||
$ javac -d out $(find multi/demo.pay -name '*.java') && java -p out -m demo.pay/pay.app.Main (25.0.4.1+1-LTS)
|
||||
card in module demo.pay
|
||||
|
||||
$ javac -d out $(find multi/demo.pay -name '*.java') && java -p out -m demo.pay/pay.app.Main (27+35)
|
||||
card in module demo.pay
|
||||
Reference in New Issue
Block a user