Summary
-------
A number of publicly exported classes in java.base are not subclassable but are not marked as such by lacking the final modifier.
Problem
-------
When a class is designed to be non-subclassable, then it should document this intent using the final modifier. See Effective Java, Third Edition, Joshua Bloch (for example, Item 19 or Item 22).
A number of publicly exported classes in java.base are "effectively non-subclassable", by having only private or package-private constructors with no actual subclasses, yet they are not marked with the final modifier.
One of these classes deserve attention beyond just lacking the final modifier:
* The class description for `java.lang.constant.DynamicCallSiteDesc` includes a note referencing subtypes, yet the class itself is has a private constructor and is non-subclassable. This class was subclassable in an early development version, but was changed to be non-subclassable before the initial released version.
Solution
--------
* Add the `final` modifier to the following classes:
* java.lang.invoke.MethodHandleProxies
* java.lang.invoke.MethodHandles
* java.lang.Runtime
* java.nio.charset.CodingErrorAction
* java.nio.charset.CoderResult
* java.lang.constant.DynamicCallSiteDesc
* java.lang.reflect.Modifier
* java.lang.runtime.ObjectMethods
* java.lang.runtime.SwitchBootstraps
* java.net.URLDecoder
* java.net.URLEncoder
* java.security.DrbgParameters
* java.util.Base64
* java.util.Collections
* java.util.FormattableFlags
* java.util.concurrent.Executors
* java.util.concurrent.locks.LockSupport
* java.net.InterfaceAddress
* java.lang.module.ModuleDescriptor
* java.lang.Package
* java.io.OptionalDataException
* Update the class description for `java.lang.constant.DynamicCallSiteDesc` to not reference subclasses in the note about immutability and object identity.
Specification
-------------
The specification diff below may easier to review in this draft PR: https://github.com/openjdk/jdk/pull/22389/files
```
diff --git a/src/java.base/share/classes/java/io/OptionalDataException.java b/src/java.base/share/classes/java/io/OptionalDataException.java
index f98bf079e8e..37736090092 100644
--- a/src/java.base/share/classes/java/io/OptionalDataException.java
+++ b/src/java.base/share/classes/java/io/OptionalDataException.java
@@ -44,7 +44,7 @@
*
* @since 1.1
*/
-public class OptionalDataException extends ObjectStreamException {
+public final class OptionalDataException extends ObjectStreamException {
@java.io.Serial
private static final long serialVersionUID = -8011121865681257820L;
diff --git a/src/java.base/share/classes/java/lang/Package.java b/src/java.base/share/classes/java/lang/Package.java
index 424c390c8ef..e3121cfa9a7 100644
--- a/src/java.base/share/classes/java/lang/Package.java
+++ b/src/java.base/share/classes/java/lang/Package.java
@@ -113,7 +113,7 @@
*
* @since 1.2
*/
-public class Package extends NamedPackage implements java.lang.reflect.AnnotatedElement {
+public final class Package extends NamedPackage implements java.lang.reflect.AnnotatedElement {
/**
* Return the name of this package.
*
diff --git a/src/java.base/share/classes/java/lang/Runtime.java b/src/java.base/share/classes/java/lang/Runtime.java
index 2f8f003af7c..d5cbbbc2423 100644
--- a/src/java.base/share/classes/java/lang/Runtime.java
+++ b/src/java.base/share/classes/java/lang/Runtime.java
@@ -120,7 +120,7 @@
* @since 1.0
*/
-public class Runtime {
+public final class Runtime {
private static final Runtime currentRuntime = new Runtime();
private static Version version;
diff --git a/src/java.base/share/classes/java/lang/constant/DynamicCallSiteDesc.java b/src/java.base/share/classes/java/lang/constant/DynamicCallSiteDesc.java
index 0cfbcd4abf5..76cef1985b3 100644
--- a/src/java.base/share/classes/java/lang/constant/DynamicCallSiteDesc.java
+++ b/src/java.base/share/classes/java/lang/constant/DynamicCallSiteDesc.java
@@ -41,12 +41,12 @@
* A <a href="package-summary.html#nominal">nominal descriptor</a> for an
* {@code invokedynamic} call site.
*
- * <p>Concrete subtypes of {@linkplain DynamicCallSiteDesc} should be immutable
- * and their behavior should not rely on object identity.
+ * <p>A {@code DynamicCallSiteDesc} is immutable and its behavior does not
+ * rely on object identity.
*
* @since 12
*/
-public class DynamicCallSiteDesc {
+public final class DynamicCallSiteDesc {
private final DirectMethodHandleDesc bootstrapMethod;
private final ConstantDesc[] bootstrapArgs;
diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java
index 0ab2dbfdae9..9d93d8e549c 100644
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java
@@ -69,7 +69,7 @@
*
* @since 1.7
*/
-public class MethodHandleProxies {
+public final class MethodHandleProxies {
private MethodHandleProxies() { } // do not instantiate
diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
index 7542b0e513a..db64c5e81a2 100644
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
@@ -84,7 +84,7 @@
* @author John Rose, JSR 292 EG
* @since 1.7
*/
-public class MethodHandles {
+public final class MethodHandles {
private MethodHandles() { } // do not instantiate
diff --git a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
index b38d1b0cc87..4b063b3d622 100644
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
@@ -91,7 +91,7 @@
* @since 9
*/
-public class ModuleDescriptor
+public final class ModuleDescriptor
implements Comparable<ModuleDescriptor>
{
diff --git a/src/java.base/share/classes/java/lang/reflect/Modifier.java b/src/java.base/share/classes/java/lang/reflect/Modifier.java
index 4b13d19f85e..d46aa6a4b11 100644
--- a/src/java.base/share/classes/java/lang/reflect/Modifier.java
+++ b/src/java.base/share/classes/java/lang/reflect/Modifier.java
@@ -52,7 +52,7 @@
* @author Kenneth Russell
* @since 1.1
*/
-public class Modifier {
+public final class Modifier {
/**
* Do not call.
*/
diff --git a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
index 51ee21e46b3..277f36dc486 100644
--- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
+++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
@@ -48,7 +48,7 @@
*
* @since 16
*/
-public class ObjectMethods {
+public final class ObjectMethods {
private ObjectMethods() { }
diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
index 53f1572fa74..f826f1e2e72 100644
--- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
+++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
@@ -74,7 +74,7 @@
*
* @since 21
*/
-public class SwitchBootstraps {
+public final class SwitchBootstraps {
private SwitchBootstraps() {}
diff --git a/src/java.base/share/classes/java/net/InterfaceAddress.java b/src/java.base/share/classes/java/net/InterfaceAddress.java
index f5b76ec9f90..9062347d074 100644
--- a/src/java.base/share/classes/java/net/InterfaceAddress.java
+++ b/src/java.base/share/classes/java/net/InterfaceAddress.java
@@ -36,7 +36,7 @@
* @see java.net.NetworkInterface
* @since 1.6
*/
-public class InterfaceAddress {
+public final class InterfaceAddress {
private InetAddress address = null;
private Inet4Address broadcast = null;
private short maskLength = 0;
diff --git a/src/java.base/share/classes/java/net/URLDecoder.java b/src/java.base/share/classes/java/net/URLDecoder.java
index e2070458e2b..aa22e740d67 100644
--- a/src/java.base/share/classes/java/net/URLDecoder.java
+++ b/src/java.base/share/classes/java/net/URLDecoder.java
@@ -81,7 +81,7 @@
* @since 1.2
*/
-public class URLDecoder {
+public final class URLDecoder {
/**
* Do not call.
diff --git a/src/java.base/share/classes/java/net/URLEncoder.java b/src/java.base/share/classes/java/net/URLEncoder.java
index 3c02dd50e6e..88be16862c9 100644
--- a/src/java.base/share/classes/java/net/URLEncoder.java
+++ b/src/java.base/share/classes/java/net/URLEncoder.java
@@ -84,7 +84,7 @@
* @author Herb Jellinek
* @since 1.0
*/
-public class URLEncoder {
+public final class URLEncoder {
private static final IntPredicate DONT_NEED_ENCODING;
static {
diff --git a/src/java.base/share/classes/java/nio/charset/CoderResult.java b/src/java.base/share/classes/java/nio/charset/CoderResult.java
index fcce8774171..d5dd397d465 100644
--- a/src/java.base/share/classes/java/nio/charset/CoderResult.java
+++ b/src/java.base/share/classes/java/nio/charset/CoderResult.java
@@ -81,7 +81,7 @@
* @since 1.4
*/
-public class CoderResult {
+public final class CoderResult {
private static final int CR_UNDERFLOW = 0;
private static final int CR_OVERFLOW = 1;
diff --git a/src/java.base/share/classes/java/nio/charset/CodingErrorAction.java b/src/java.base/share/classes/java/nio/charset/CodingErrorAction.java
index c708e634de1..617f30305dc 100644
--- a/src/java.base/share/classes/java/nio/charset/CodingErrorAction.java
+++ b/src/java.base/share/classes/java/nio/charset/CodingErrorAction.java
@@ -40,7 +40,7 @@
* @since 1.4
*/
-public class CodingErrorAction {
+public final class CodingErrorAction {
private String name;
diff --git a/src/java.base/share/classes/java/security/DrbgParameters.java b/src/java.base/share/classes/java/security/DrbgParameters.java
index d979aba9531..e53413e9d09 100644
--- a/src/java.base/share/classes/java/security/DrbgParameters.java
+++ b/src/java.base/share/classes/java/security/DrbgParameters.java
@@ -232,7 +232,7 @@
*
* @since 9
*/
-public class DrbgParameters {
+public final class DrbgParameters {
private DrbgParameters() {
// This class should not be instantiated
diff --git a/src/java.base/share/classes/java/util/Base64.java b/src/java.base/share/classes/java/util/Base64.java
index 60b6c18c078..f053a94e74c 100644
--- a/src/java.base/share/classes/java/util/Base64.java
+++ b/src/java.base/share/classes/java/util/Base64.java
@@ -81,7 +81,7 @@
* @since 1.8
*/
-public class Base64 {
+public final class Base64 {
private Base64() {}
diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java
index 4ec5756f712..c6503ab2287 100644
--- a/src/java.base/share/classes/java/util/Collections.java
+++ b/src/java.base/share/classes/java/util/Collections.java
@@ -82,7 +82,7 @@
* @since 1.2
*/
-public class Collections {
+public final class Collections {
// Suppresses default constructor, ensuring non-instantiability.
private Collections() {
}
diff --git a/src/java.base/share/classes/java/util/FormattableFlags.java b/src/java.base/share/classes/java/util/FormattableFlags.java
index 92c020f9991..8939a52b397 100644
--- a/src/java.base/share/classes/java/util/FormattableFlags.java
+++ b/src/java.base/share/classes/java/util/FormattableFlags.java
@@ -33,7 +33,7 @@
*
* @since 1.5
*/
-public class FormattableFlags {
+public final class FormattableFlags {
// Explicit instantiation of this class is prohibited.
private FormattableFlags() {}
diff --git a/src/java.base/share/classes/java/util/concurrent/Executors.java b/src/java.base/share/classes/java/util/concurrent/Executors.java
index f804e225790..49cf497c20b 100644
--- a/src/java.base/share/classes/java/util/concurrent/Executors.java
+++ b/src/java.base/share/classes/java/util/concurrent/Executors.java
@@ -68,7 +68,7 @@
* @since 1.5
* @author Doug Lea
*/
-public class Executors {
+public final class Executors {
/**
* Creates a thread pool that reuses a fixed number of threads
diff --git a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
index d6c45c7b04f..917678b5f1e 100644
--- a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
+++ b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
@@ -139,7 +139,7 @@
*
* @since 1.5
*/
-public class LockSupport {
+public final class LockSupport {
private LockSupport() {} // Cannot be instantiated.
private static void setBlocker(Thread t, Object arg) {
```