22
33import org .junit .jupiter .api .Assertions ;
44import org .junit .jupiter .api .Test ;
5- import org . junit . jupiter . api . condition . DisabledOnJre ;
6- import org . junit . jupiter . api . condition . JRE ;
5+ import pl . mperor . lab . java . lang . JavaBean ;
6+ import pl . mperor . lab . java . sealed .* ;
77
8+ import java .applet .Applet ;
9+ import java .io .*;
810import java .lang .reflect .Field ;
911import java .lang .reflect .InaccessibleObjectException ;
1012import java .util .HexFormat ;
11- import java .util .ServiceLoader ;
12- import java .util .random .RandomGenerator ;
13+ import java .util .Set ;
1314import java .util .random .RandomGeneratorFactory ;
1415
15- /**
16- * Java 17 (September 2021)
17- */
16+ /// Java 17™ (September 2021)
17+ /// [JDK 17](https://openjdk.org/projects/jdk/17)
18+ ///
19+ /// - STANDARD FEATURES:
20+ /// - 409: Sealed Classes
21+ /// - 403: Strongly Encapsulate JDK Internals
22+ /// - 356: Enhanced Pseudo-Random Number Generators
23+ /// - 306: Restore Always-Strict Floating-Point Semantics
24+ /// - 398: Deprecate the Applet API for Removal
25+ /// - 411: Deprecate the Security Manager for Removal
26+ /// - 407: Remove RMI Activation
27+ /// - 410: Remove the Experimental AOT and JIT Compiler
28+ /// - 415: Context-Specific Deserialization Filters
29+ /// - 382: New macOS Rendering Pipeline
30+ /// - 391: macOS/AArch64 Port
31+ ///
32+ /// - PREVIEW & INCUBATOR:
33+ /// - 406: Pattern Matching for switch (Preview)
34+ /// - 412: Foreign Function & Memory API (Incubator)
35+ /// - 414: Vector API (Second Incubator)
1836public class Java17 {
1937
38+ @ Test
39+ public void testSealedClassesAndInterfaces () {
40+ Assertions .assertTrue (Sealed .class .isSealed ());
41+ Assertions .assertFalse (NonSealed .class .isSealed ());
42+ Assertions .assertFalse (Final .class .isSealed ());
43+ Assertions .assertTrue (AlsoSealed .class .isSealed ());
44+ Assertions .assertFalse (AlsoFinal .class .isSealed ());
45+ Assertions .assertFalse (ImplicitlyFinal .class .isSealed ());
46+
47+ Assertions .assertEquals ("final" , switchSealed (new Final ()));
48+ Assertions .assertEquals ("non-sealed" , switchSealed (new NonSealed ()));
49+ Assertions .assertEquals ("sealed" , switchSealed (new AlsoSealed ()));
50+ Assertions .assertEquals ("implicitly final" , switchSealed (new ImplicitlyFinal ()));
51+
52+ SealedClient client = new SealedClient (new NonSealedChild ());
53+ Assertions .assertEquals ("non-sealed" , client .call ());
54+ }
55+
56+ private static String switchSealed (Sealed sealed ) {
57+ return switch (sealed ) {
58+ case AlsoSealed a -> a .alsoSealedMethod ();
59+ case Final f -> f .finalMethod ();
60+ case NonSealed ns -> ns .nonSealedMethod ();
61+ case ImplicitlyFinal r -> r .implicitlyFinalMethod ();
62+ };
63+ }
64+
65+ private static class NonSealedChild extends NonSealed {}
66+
67+ private static class NotPermittedSealedChild /* implements Sealed */ {}
68+
69+ private static class SealedClient {
70+ private Sealed s ;
71+
72+ public SealedClient (Sealed s ) {
73+ this .s = s ;
74+ }
75+
76+ public String call () {
77+ return switchSealed (s );
78+ }
79+ }
80+
2081 @ Test
2182 public void testStronglyEncapsulatedInternals () {
2283 Assertions .assertThrows (InaccessibleObjectException .class , () -> deepLookIntoStringBytes ("Hello" ));
@@ -28,48 +89,20 @@ private static byte[] deepLookIntoStringBytes(String string) throws IllegalAcces
2889 return (byte []) valueField .get (string );
2990 }
3091
31- @ Test
32- public void testSwitchPatternMatching () {
33- Assertions .assertEquals ("String: Hello" , switchOverClasses ("Hello" ));
34- Assertions .assertEquals ("int: 1" , switchOverClasses (1 ));
35- Assertions .assertEquals ("long: 13" , switchOverClasses (13L ));
36- Assertions .assertEquals ("boolean: true" , switchOverClasses (true ));
37- Assertions .assertEquals ("null" , switchOverClasses (null ));
38- record Person (String name , String surname ) {
39- @ Override
40- public String toString () {
41- return "%s %s" .formatted (name , surname );
42- }
43- }
44- Assertions .assertEquals ("Object: John Doe" , switchOverClasses (new Person ("John" , "Doe" )));
45- }
46-
47- private static String switchOverClasses (Object obj ) {
48- return switch (obj ) {
49- case String s -> String .format ("String: %s" , s );
50- case Integer i -> String .format ("int: %d" , i );
51- case Long l -> String .format ("long: %d" , l );
52- case Boolean b -> String .format ("boolean: %s" , b );
53- case null -> "null" ;
54- default -> "Object: " + obj ;
55- };
56- }
57-
58- @ DisabledOnJre (JRE .JAVA_23 )
5992 @ Test
6093 public void testRandomGeneratorFactory () {
61- var generators = ServiceLoader .load (RandomGenerator .class ).stream ()
62- .map (provider -> provider .type ().getCanonicalName ())
94+ var defaultRandomGenerator = RandomGeneratorFactory .getDefault ().create ();
95+ Assertions .assertEquals ("jdk.internal.random.L32X64MixRandom" , defaultRandomGenerator .getClass ().getCanonicalName ());
96+
97+ var generators = RandomGeneratorFactory .all ()
98+ .map (fac -> fac .create ().getClass ().getCanonicalName ())
6399 .peek (System .out ::println )
64100 .toList ();
65-
66101 Assertions .assertTrue (generators .contains ("java.util.Random" ));
67- var defaultRandomGenerator = RandomGeneratorFactory .getDefault ().create ();
68- Assertions .assertEquals ("jdk.random.L32X64MixRandom" , defaultRandomGenerator .getClass ().getCanonicalName ());
69102 }
70103
71104 @ Test
72- void testHexFormat () {
105+ public void testHexFormat () {
73106 HexFormat hexFormat = HexFormat .of ();
74107 String hex = hexFormat .formatHex (new byte []{0x1A , 0x2B , 0x3C });
75108 Assertions .assertEquals ("1a2b3c" , hex );
@@ -78,4 +111,48 @@ void testHexFormat() {
78111 Assertions .assertEquals (16 + 10 , HexFormat .fromHexDigits ("1a" ));
79112 }
80113
114+ @ Test
115+ public void testAlwaysStrictFloatingPointSemantics () {
116+ Assertions .assertEquals (4 , strictPower (2 , 2 ));
117+ }
118+
119+ // warning: [strictfp] as of release 17, all floating-point expressions are evaluated strictly and 'strictfp' is not required
120+ @ SuppressWarnings ("strictfp" )
121+ public static strictfp double strictPower (double a , double b ) {
122+ return Math .pow (a , b );
123+ }
124+
125+ @ SuppressWarnings ("removal" )
126+ @ Test
127+ public void testDeprecated () {
128+ // warning: [removal] Applet in java.applet has been deprecated and marked for removal
129+ Applet appletDeprecatedForRemoval ;
130+ // warning: [removal] SecurityManager in java.lang has been deprecated and marked for removal
131+ SecurityManager securityManager = System .getSecurityManager ();
132+ }
133+
134+ @ Test
135+ public void testRmiActivation () {
136+ Assertions .assertFalse (ModuleLayer .boot ().findModule ("java.rmi" ).stream ().map (Module ::getPackages )
137+ .flatMap (Set ::stream )
138+ .anyMatch ("java.rmi.activation" ::equals ));
139+ }
140+
141+ @ Test
142+ public void testDeserializationFilters () throws IOException , ClassNotFoundException {
143+ var file = new File ("src/test/resources/bean" );
144+
145+ var basePackageFilter = ObjectInputFilter .Config .createFilter ("java.base/*;!*" );
146+ try (ObjectInputStream in = new ObjectInputStream (new FileInputStream (file ))) {
147+ in .setObjectInputFilter (basePackageFilter );
148+ Assertions .assertThrows (InvalidClassException .class , () -> in .readObject ());
149+ }
150+
151+ ObjectInputFilter onlyJavaBeansFilter = ObjectInputFilter .allowFilter (JavaBean .class ::equals , ObjectInputFilter .Status .REJECTED );
152+ try (ObjectInputStream in = new ObjectInputStream (new FileInputStream (file ))) {
153+ in .setObjectInputFilter (onlyJavaBeansFilter );
154+ Assertions .assertNotNull (in .readObject ());
155+ }
156+ }
157+
81158}
0 commit comments