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+ ///
34+ /// - 406: Pattern Matching for switch (Preview)
35+ /// - 412: Foreign Function & Memory API (Incubator)
36+ /// - 414: Vector API (Second Incubator)
1837public class Java17 {
1938
39+ @ Test
40+ public void testSealedClassesAndInterfaces () {
41+ Assertions .assertTrue (Sealed .class .isSealed ());
42+ Assertions .assertFalse (NonSealed .class .isSealed ());
43+ Assertions .assertFalse (Final .class .isSealed ());
44+ Assertions .assertTrue (AlsoSealed .class .isSealed ());
45+ Assertions .assertFalse (AlsoFinal .class .isSealed ());
46+ Assertions .assertFalse (ImplicitlyFinal .class .isSealed ());
47+
48+ Assertions .assertEquals ("final" , switchSealed (new Final ()));
49+ Assertions .assertEquals ("non-sealed" , switchSealed (new NonSealed ()));
50+ Assertions .assertEquals ("sealed" , switchSealed (new AlsoSealed ()));
51+ Assertions .assertEquals ("implicitly final" , switchSealed (new ImplicitlyFinal ()));
52+
53+ SealedClient client = new SealedClient (new NonSealedChild ());
54+ Assertions .assertEquals ("non-sealed" , client .call ());
55+ }
56+
57+ private static String switchSealed (Sealed sealed ) {
58+ return switch (sealed ) {
59+ case AlsoSealed a -> a .alsoSealedMethod ();
60+ case Final f -> f .finalMethod ();
61+ case NonSealed ns -> ns .nonSealedMethod ();
62+ case ImplicitlyFinal r -> r .implicitlyFinalMethod ();
63+ };
64+ }
65+
66+ private static class NonSealedChild extends NonSealed {}
67+
68+ private static class NotPermittedSealedChild /* implements Sealed */ {}
69+
70+ private static class SealedClient {
71+ private Sealed s ;
72+
73+ public SealedClient (Sealed s ) {
74+ this .s = s ;
75+ }
76+
77+ public String call () {
78+ return switchSealed (s );
79+ }
80+ }
81+
2082 @ Test
2183 public void testStronglyEncapsulatedInternals () {
2284 Assertions .assertThrows (InaccessibleObjectException .class , () -> deepLookIntoStringBytes ("Hello" ));
@@ -28,48 +90,20 @@ private static byte[] deepLookIntoStringBytes(String string) throws IllegalAcces
2890 return (byte []) valueField .get (string );
2991 }
3092
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 )
5993 @ Test
6094 public void testRandomGeneratorFactory () {
61- var generators = ServiceLoader .load (RandomGenerator .class ).stream ()
62- .map (provider -> provider .type ().getCanonicalName ())
95+ var defaultRandomGenerator = RandomGeneratorFactory .getDefault ().create ();
96+ Assertions .assertEquals ("jdk.internal.random.L32X64MixRandom" , defaultRandomGenerator .getClass ().getCanonicalName ());
97+
98+ var generators = RandomGeneratorFactory .all ()
99+ .map (fac -> fac .create ().getClass ().getCanonicalName ())
63100 .peek (System .out ::println )
64101 .toList ();
65-
66102 Assertions .assertTrue (generators .contains ("java.util.Random" ));
67- var defaultRandomGenerator = RandomGeneratorFactory .getDefault ().create ();
68- Assertions .assertEquals ("jdk.random.L32X64MixRandom" , defaultRandomGenerator .getClass ().getCanonicalName ());
69103 }
70104
71105 @ Test
72- void testHexFormat () {
106+ public void testHexFormat () {
73107 HexFormat hexFormat = HexFormat .of ();
74108 String hex = hexFormat .formatHex (new byte []{0x1A , 0x2B , 0x3C });
75109 Assertions .assertEquals ("1a2b3c" , hex );
@@ -78,4 +112,48 @@ void testHexFormat() {
78112 Assertions .assertEquals (16 + 10 , HexFormat .fromHexDigits ("1a" ));
79113 }
80114
115+ @ Test
116+ public void testAlwaysStrictFloatingPointSemantics () {
117+ Assertions .assertEquals (4 , strictPower (2 , 2 ));
118+ }
119+
120+ // warning: [strictfp] as of release 17, all floating-point expressions are evaluated strictly and 'strictfp' is not required
121+ @ SuppressWarnings ("strictfp" )
122+ public static strictfp double strictPower (double a , double b ) {
123+ return Math .pow (a , b );
124+ }
125+
126+ @ SuppressWarnings ("removal" )
127+ @ Test
128+ public void testDeprecated () {
129+ // warning: [removal] Applet in java.applet has been deprecated and marked for removal
130+ Applet appletDeprecatedForRemoval ;
131+ // warning: [removal] SecurityManager in java.lang has been deprecated and marked for removal
132+ SecurityManager securityManager = System .getSecurityManager ();
133+ }
134+
135+ @ Test
136+ public void testRmiActivation () {
137+ Assertions .assertFalse (ModuleLayer .boot ().findModule ("java.rmi" ).stream ().map (Module ::getPackages )
138+ .flatMap (Set ::stream )
139+ .anyMatch ("java.rmi.activation" ::equals ));
140+ }
141+
142+ @ Test
143+ public void testDeserializationFilters () throws IOException , ClassNotFoundException {
144+ var file = new File ("src/test/resources/bean" );
145+
146+ var basePackageFilter = ObjectInputFilter .Config .createFilter ("java.base/*;!*" );
147+ try (ObjectInputStream in = new ObjectInputStream (new FileInputStream (file ))) {
148+ in .setObjectInputFilter (basePackageFilter );
149+ Assertions .assertThrows (InvalidClassException .class , () -> in .readObject ());
150+ }
151+
152+ ObjectInputFilter onlyJavaBeansFilter = ObjectInputFilter .allowFilter (JavaBean .class ::equals , ObjectInputFilter .Status .REJECTED );
153+ try (ObjectInputStream in = new ObjectInputStream (new FileInputStream (file ))) {
154+ in .setObjectInputFilter (onlyJavaBeansFilter );
155+ Assertions .assertNotNull (in .readObject ());
156+ }
157+ }
158+
81159}
0 commit comments