JDK-8283669 : Update IllegalFormatException to use sealed classes
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.util
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 19
  • Submitted: 2022-03-25
  • Updated: 2022-03-26
  • Resolved: 2022-03-25
Related Reports
CSR :  
Description
Summary
-------

Update the `IllegalFormatException` class to be a sealed class and its subclasses with public constructors non-sealed.

Problem
-------

Conceptually  `IllegalFormatException` is a sealed class, but is not marked as such.

Solution
--------

Upgrade the class in question to be sealed.

Specification
-------------

Note that `IllegalFormatArgumentIndexException` is a non-public class so the updates to it are not part of the specification change of this CSR.

    diff --git a/src/java.base/share/classes/java/util/DuplicateFormatFlagsException.java b/src/java.base/share/classes/java/util/DuplicateFormatFlagsException.java
    index 356ea209c92..ebeb3d5c44c 100644
    --- a/src/java.base/share/classes/java/util/DuplicateFormatFlagsException.java
    +++ b/src/java.base/share/classes/java/util/DuplicateFormatFlagsException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -35,7 +35,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class DuplicateFormatFlagsException extends IllegalFormatException {
    +public non-sealed class DuplicateFormatFlagsException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 18890531L;
    diff --git a/src/java.base/share/classes/java/util/FormatFlagsConversionMismatchException.java b/src/java.base/share/classes/java/util/FormatFlagsConversionMismatchException.java
    index 6f2464d4fb6..cca7b3eb553 100644
    --- a/src/java.base/share/classes/java/util/FormatFlagsConversionMismatchException.java
    +++ b/src/java.base/share/classes/java/util/FormatFlagsConversionMismatchException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -34,7 +34,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class FormatFlagsConversionMismatchException
    +public non-sealed class FormatFlagsConversionMismatchException
         extends IllegalFormatException
     {
         @java.io.Serial
    diff --git a/src/java.base/share/classes/java/util/IllegalFormatArgumentIndexException.java b/src/java.base/share/classes/java/util/IllegalFormatArgumentIndexException.java
    index 02d5449098d..64b24411592 100644
    --- a/src/java.base/share/classes/java/util/IllegalFormatArgumentIndexException.java
    +++ b/src/java.base/share/classes/java/util/IllegalFormatArgumentIndexException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -33,7 +33,7 @@ package java.util;
      *
      * @since 16
      */
    -class IllegalFormatArgumentIndexException extends IllegalFormatException {
    +final class IllegalFormatArgumentIndexException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 4191767811181838112L;
    diff --git a/src/java.base/share/classes/java/util/IllegalFormatCodePointException.java b/src/java.base/share/classes/java/util/IllegalFormatCodePointException.java
    index 14ec9137f1e..2f1803d220e 100644
    --- a/src/java.base/share/classes/java/util/IllegalFormatCodePointException.java
    +++ b/src/java.base/share/classes/java/util/IllegalFormatCodePointException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -36,7 +36,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class IllegalFormatCodePointException extends IllegalFormatException {
    +public non-sealed class IllegalFormatCodePointException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 19080630L;
    diff --git a/src/java.base/share/classes/java/util/IllegalFormatConversionException.java b/src/java.base/share/classes/java/util/IllegalFormatConversionException.java
    index 2dacf2094b4..67237f63cdf 100644
    --- a/src/java.base/share/classes/java/util/IllegalFormatConversionException.java
    +++ b/src/java.base/share/classes/java/util/IllegalFormatConversionException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -35,7 +35,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class IllegalFormatConversionException extends IllegalFormatException {
    +public non-sealed class IllegalFormatConversionException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 17000126L;
    diff --git a/src/java.base/share/classes/java/util/IllegalFormatException.java b/src/java.base/share/classes/java/util/IllegalFormatException.java
    index 97b85cc107e..f5be4ad268b 100644
    --- a/src/java.base/share/classes/java/util/IllegalFormatException.java
    +++ b/src/java.base/share/classes/java/util/IllegalFormatException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -33,7 +33,19 @@ package java.util;
      *
      * @since 1.5
      */
    -public class IllegalFormatException extends IllegalArgumentException {
    +public sealed class IllegalFormatException extends IllegalArgumentException
    +    permits DuplicateFormatFlagsException,
    +            FormatFlagsConversionMismatchException,
    +            IllegalFormatArgumentIndexException,
    +            IllegalFormatCodePointException,
    +            IllegalFormatConversionException,
    +            IllegalFormatFlagsException,
    +            IllegalFormatPrecisionException,
    +            IllegalFormatWidthException,
    +            MissingFormatArgumentException,
    +            MissingFormatWidthException,
    +            UnknownFormatConversionException,
    +            UnknownFormatFlagsException {
     
         @java.io.Serial
         private static final long serialVersionUID = 18830826L;
    diff --git a/src/java.base/share/classes/java/util/IllegalFormatFlagsException.java b/src/java.base/share/classes/java/util/IllegalFormatFlagsException.java
    index 38e4e2d577e..408fa1efa5c 100644
    --- a/src/java.base/share/classes/java/util/IllegalFormatFlagsException.java
    +++ b/src/java.base/share/classes/java/util/IllegalFormatFlagsException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -34,7 +34,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class IllegalFormatFlagsException extends IllegalFormatException {
    +public non-sealed class IllegalFormatFlagsException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 790824L;
    diff --git a/src/java.base/share/classes/java/util/IllegalFormatPrecisionException.java b/src/java.base/share/classes/java/util/IllegalFormatPrecisionException.java
    index bdf3de6f528..4527953790c 100644
    --- a/src/java.base/share/classes/java/util/IllegalFormatPrecisionException.java
    +++ b/src/java.base/share/classes/java/util/IllegalFormatPrecisionException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -34,7 +34,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class IllegalFormatPrecisionException extends IllegalFormatException {
    +public non-sealed class IllegalFormatPrecisionException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 18711008L;
    diff --git a/src/java.base/share/classes/java/util/IllegalFormatWidthException.java b/src/java.base/share/classes/java/util/IllegalFormatWidthException.java
    index e00ec01eee0..df8d59b9e81 100644
    --- a/src/java.base/share/classes/java/util/IllegalFormatWidthException.java
    +++ b/src/java.base/share/classes/java/util/IllegalFormatWidthException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -33,7 +33,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class IllegalFormatWidthException extends IllegalFormatException {
    +public non-sealed class IllegalFormatWidthException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 16660902L;
    diff --git a/src/java.base/share/classes/java/util/MissingFormatArgumentException.java b/src/java.base/share/classes/java/util/MissingFormatArgumentException.java
    index dcb0847833b..6363566216b 100644
    --- a/src/java.base/share/classes/java/util/MissingFormatArgumentException.java
    +++ b/src/java.base/share/classes/java/util/MissingFormatArgumentException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -36,7 +36,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class MissingFormatArgumentException extends IllegalFormatException {
    +public non-sealed class MissingFormatArgumentException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 19190115L;
    diff --git a/src/java.base/share/classes/java/util/MissingFormatWidthException.java b/src/java.base/share/classes/java/util/MissingFormatWidthException.java
    index 0ce9c3af83c..b553a3876ac 100644
    --- a/src/java.base/share/classes/java/util/MissingFormatWidthException.java
    +++ b/src/java.base/share/classes/java/util/MissingFormatWidthException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -34,7 +34,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class MissingFormatWidthException extends IllegalFormatException {
    +public non-sealed class MissingFormatWidthException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 15560123L;
    diff --git a/src/java.base/share/classes/java/util/UnknownFormatConversionException.java b/src/java.base/share/classes/java/util/UnknownFormatConversionException.java
    index 07519ade170..13d428e0b60 100644
    --- a/src/java.base/share/classes/java/util/UnknownFormatConversionException.java
    +++ b/src/java.base/share/classes/java/util/UnknownFormatConversionException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -34,7 +34,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class UnknownFormatConversionException extends IllegalFormatException {
    +public non-sealed class UnknownFormatConversionException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 19060418L;
    diff --git a/src/java.base/share/classes/java/util/UnknownFormatFlagsException.java b/src/java.base/share/classes/java/util/UnknownFormatFlagsException.java
    index c498711fa21..93eaf85b389 100644
    --- a/src/java.base/share/classes/java/util/UnknownFormatFlagsException.java
    +++ b/src/java.base/share/classes/java/util/UnknownFormatFlagsException.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -34,7 +34,7 @@ package java.util;
      *
      * @since 1.5
      */
    -public class UnknownFormatFlagsException extends IllegalFormatException {
    +public non-sealed class UnknownFormatFlagsException extends IllegalFormatException {
     
         @java.io.Serial
         private static final long serialVersionUID = 19370506L;




Comments
Reviewing additionally. A question came up in the PR/email comments regarding the rationale for this and other changes. Instead of responding on the PR, I'll respond here, a location that is likely easier to find in the long run. The sealed classes feature of the language allows library maintainers to express *design intent* more specifically. This hierarchy of classes is set up in a particular way, where the superclass (IllegalFormatException) is intended to be extended only exception classes within the JDK itself. The way this has been expressed historically here and in several other cases is to ensure that all constructors of the superclass have package-private access. Practically speaking this constrains all subclasses to reside within the same package. In this approach, the design intent is signaled by the *absence* of protected and public constructors. Divining this is subtle, and it pretty much requires one to know about this particular implementation pattern. Using sealed classes makes this explicit. There is an additional issue with serializability (and all exception classes are serializable). Deserialization doesn't call constructors. The deserialization mechanism uses the absence of protected and public constructors to indicate that subclasses outside the package are disallowed. This is extremely subtle. Using sealed classes makes this explicit, and it allows the JVM to enforce this restriction much more reliably.
25-03-2022

Moving to Approved.
25-03-2022