Improve documentation for JEP 526: Lazy Constants

XMLWordPrintable

    • Type: CSR
    • Resolution: Approved
    • Priority: P3
    • 26
    • Component/s: core-libs
    • None
    • behavioral
    • minimal
    • The spec is updated to reflect how lazy constructs actually work.
    • Java API
    • SE

      Summary

      Minor adjustments to the documentation based on PR feedback.

      Problem

      The LazyConstant::toString was not specified correctly. Also, the specification of the returned List/Maps' toString was incorrect.

      Solution

      Update the specification

      https://github.com/openjdk/jdk/compare/6721ca4..e5399c5.diff

      diff --git a/src/java.base/share/classes/java/lang/LazyConstant.java b/src/java.base/share/classes/java/lang/LazyConstant.java
      index 5f5d37b3d1dc3..34f3d754a10f3 100644
      --- a/src/java.base/share/classes/java/lang/LazyConstant.java
      +++ b/src/java.base/share/classes/java/lang/LazyConstant.java
      @@ -41,11 +41,11 @@
        * {@linkplain LazyConstant#of(Supplier) LazyConstant.of({@code <computing function>})}.
        * When created, the lazy constant is <em>not initialized</em>, meaning it has no contents.
        * The lazy constant (of type {@code T}) can then be <em>initialized</em>
      - * (and its contents retrieved) by calling {@linkplain #get()}. The first time
      - * {@linkplain #get()} is called, the underlying <em>computing function</em>
      + * (and its contents retrieved) by calling {@linkplain #get() get()}. The first time
      + * {@linkplain #get() get()} is called, the underlying <em>computing function</em>
        * (provided at construction) will be invoked and the result will be used to initialize
        * the constant. Once a lazy constant is initialized, its contents can <em>never change</em>
      - * and will be retrieved over and over again upon subsequent {@linkplain #get() get}
      + * and will be retrieved over and over again upon subsequent {@linkplain #get() get()}
        * invocations.
        * <p>
        * Consider the following example where a lazy constant field "{@code logger}" holds
      @@ -68,11 +68,11 @@
        * <p>
        * Initially, the lazy constant is <em>not initialized</em>. When {@code logger.get()}
        * is first invoked, it evaluates the computing function and initializes the constant to
      - * the result; the result is then returned to the client. Hence, {@linkplain #get()}
      + * the result; the result is then returned to the client. Hence, {@linkplain #get() get()}
        * guarantees that the constant is <em>initialized</em> before it returns, barring
        * any exceptions.
        * <p>
      - * Furthermore, {@linkplain #get()} guarantees that, out of several threads trying to
      + * Furthermore, {@linkplain #get() get()} guarantees that, out of several threads trying to
        * invoke the computing function simultaneously, {@linkplain ##thread-safety only one is
        * ever selected} for computation. This property is crucial as evaluation of the computing
        * function may have side effects, for example, the call above to {@code Logger.create()}
      @@ -86,10 +86,6 @@
        * If the computing function recursively invokes itself (directly or indirectly via
        * the lazy constant), an {@linkplain IllegalStateException} is thrown, and the lazy
        * constant is not initialized.
      - * <p>
      - * If the computing function throws any unchecked {@linkplain Exception} or
      - * {@linkplain Error}, that {@linkplain Throwable} is propagated to the caller, and the
      - * lazy constant remains uninitialized.
        *
        * <h2 id="composition">Composing lazy constants</h2>
        * A lazy constant can depend on other lazy constants, forming a dependency graph
      @@ -158,8 +154,8 @@
        * This is only possible if there is a direct reference from a {@code static final} field
        * to a lazy constant or if there is a chain from a {@code static final} field -- via one
        * or more <em>trusted fields</em> (i.e., {@code static final} fields,
      - * {@linkplain Record record} fields, lazy constants, lazy lists, lazy maps,
      - * or final instance fields in hidden classes) -- to a lazy constant.
      + * {@linkplain Record record} fields, or final instance fields in hidden classes) --
      + * to a lazy constant.
        *
        * <h2 id="miscellaneous">Miscellaneous</h2>
        * Except for {@linkplain Object#equals(Object) equals(obj)} and
      @@ -169,8 +165,8 @@
        * @apiNote Once a lazy constant is initialized, its contents cannot ever be removed.
        *          This can be a source of an unintended memory leak. More specifically,
        *          a lazy constant {@linkplain java.lang.ref##reachability strongly references}
      - *          it contents. Hence, a lazy constant will hold its contents until
      - *          the lazy constant itself is collected (if ever).
      + *          it contents. Hence, the contents of a lazy constant will be reachable as long
      + *          as the lazy constant itself is reachable.
        *          <p>
        *          While it's possible to store an array inside a lazy constant, doing so will
        *          not result in improved access performance of the array elements. Instead, a
      @@ -181,7 +177,7 @@
        *          <p>
        *          Use in static initializers may interact with class initialization order;
        *          cyclic initialization may result in initialization errors as described
      - *          in {@jls 12.4} of <cite>The Java Language Specification</cite>.
      + *          in section {@jls 12.4} of <cite>The Java Language Specification</cite>.
        *
        * @implNote
        *           A lazy constant is free to synchronize on itself. Hence, care must be
      @@ -206,7 +202,7 @@ public sealed interface LazyConstant<T>
               permits LazyConstantImpl {
       
           /**
      -     * {@return the contents of this lazy constant if initialized, otherwise,\
      +     * {@return the contents of this lazy constant if initialized, otherwise,
            *          returns {@code other}}
            * <p>
            * This method never triggers initialization of this lazy constant and will observe
      @@ -243,14 +239,19 @@ public sealed interface LazyConstant<T>
       
           /**
            * {@return if this lazy constant is the same as the provided {@code obj}}
      -     *
      +     * <p>
      +     * In other words, equals compares the identity of this lazy constant and {@code obj}
      +     * to determine equality. Hence, two lazy constants with the same contents are
      +     * <em>not</em> equal.
      +     * <p>
            * This method never triggers initialization of this lazy constant.
            */
           @Override
           boolean equals(Object obj);
       
           /**
      -     * {@return the {@linkplain System#identityHashCode(Object)} for this lazy constant}
      +     * {@return the {@linkplain System#identityHashCode(Object) identity hash code} for
      +     *          this lazy constant}
            *
            * This method never triggers initialization of this lazy constant.
            */
      @@ -258,13 +259,14 @@ public sealed interface LazyConstant<T>
           int hashCode();
       
           /**
      -     * {@return a non-initializing string suitable for debugging}
      +     * {@return a string suitable for debugging}
            * <p>
            * This method never triggers initialization of this lazy constant and will observe
            * initialization by other threads atomically (i.e., it observes the
            * contents if and only if the initialization has already completed).
            * <p>
      -     * If this lazy constant is initialized, the {@linkplain Object#toString()} of the
      +     * If this lazy constant is initialized, an implementation-dependent string
      +     * containing the {@linkplain Object#toString()} of the
            * contents will be returned; otherwise, an implementation-dependent string is
            * returned that indicates this lazy constant is not yet initialized.
            */
      @@ -274,18 +276,20 @@ public sealed interface LazyConstant<T>
           // Factory
       
           /**
      -     * {@return a lazy constant to be computed later via the provided
      +     * {@return a lazy constant whose contents is to be computed later via the provided
            *          {@code computingFunction}}
            * <p>
            * The returned lazy constant strongly references the provided
      -     * {@code computingFunction} until initialization completes successfully; after
      -     * which the computing function is no longer strongly referenced and becomes
      -     * eligible for garbage collection.
      +     * {@code computingFunction} at least until initialization completes successfully.
            * <p>
            * If the provided computing function is already an instance of
            * {@code LazyConstant}, the method is free to return the provided computing function
            * directly.
            *
      +     * @implNote  after initialization completes successfully, the computing function is
      +     *            no longer strongly referenced and becomes eligible for
      +     *            garbage collection.
      +     *
            * @param computingFunction in the form of a {@linkplain Supplier} to be used
            *                          to initialize the constant
            * @param <T>               type of the constant
      diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java
      index 238dc34965c76..6ab80b83ef86a 100644
      --- a/src/java.base/share/classes/java/util/List.java
      +++ b/src/java.base/share/classes/java/util/List.java
      @@ -1197,7 +1197,7 @@ static <E> List<E> copyOf(Collection<? extends E> coll) {
           }
       
           /**
      -     * {@return a new lazily computed list with the provided {@code size}}
      +     * {@return a new lazily computed list of the provided {@code size}}
            * <p>
            * The returned list is an {@linkplain Collection##unmodifiable unmodifiable} list
            * with the provided {@code size}. The list's elements are lazily computed via the
      @@ -1207,7 +1207,7 @@ static <E> List<E> copyOf(Collection<? extends E> coll) {
            * The provided computing function is guaranteed to be successfully
            * invoked at most once per list index, even in a multi-threaded environment.
            * Competing threads accessing an element already under computation will block until
      -     * an element is computed or an exception is thrown by the computing thread.
      +     * an element is computed or the computing function completes abnormally
            * <p>
            * If invoking the provided computing function throws an exception, it is rethrown
            * to the initial caller and no value for the element is recorded.
      @@ -1218,8 +1218,8 @@ static <E> List<E> copyOf(Collection<? extends E> coll) {
            * cannot contain {@code null} elements. Clients that want to use nullable elements
            * can wrap elements into an {@linkplain Optional} holder.
            * <p>
      -     * Any {@link List#subList(int, int) subList()} or {@link List#reversed()} views
      -     * of the returned list are also lazily computed.
      +     * The elements of any {@link List#subList(int, int) subList()} or
      +     * {@link List#reversed()} views of the returned list are also lazily computed.
            * <p>
            * The returned list and its {@link List#subList(int, int) subList()} or
            * {@link List#reversed()} views implement the {@link RandomAccess} interface.
      @@ -1228,19 +1228,22 @@ static <E> List<E> copyOf(Collection<? extends E> coll) {
            * lazy list for the same index, an {@linkplain IllegalStateException}
            * will be thrown.
            * <p>
      -     * The returned list's {@linkplain Object#equals(Object) equals()} and
      -     * {@linkplain Object#hashCode() hashCode()} methods may trigger initialization of one
      -     * or more lazy elements. The returned list's {@linkplain Object#toString() toString()}
      -     * method never triggers initialization of elements. Instead, an
      -     * implementation-dependent string is returned for uninitialized elements.
      +     * The returned list's {@linkplain Object Object methods};
      +     * {@linkplain Object#equals(Object) equals()},
      +     * {@linkplain Object#hashCode() hashCode()}, and
      +     * {@linkplain Object#toString() toString()} methods may trigger initialization of
      +     * one or more lazy elements.
            * <p>
            * The returned lazy list strongly references its computing
      -     * function used to compute elements only so long as there are uncomputed elements
      -     * after which the computing function is not strongly referenced
      -     * anymore and may be collected.
      +     * function used to compute elements at least so long as there are uninitialized
      +     * elements.
            * <p>
            * The returned List is <em>not</em> {@linkplain Serializable}.
            *
      +     * @implNote  after all elements have been initialized successfully, the computing
      +     *            function is no longer strongly referenced and becomes eligible for
      +     *            garbage collection.
      +     *
            * @param size              the size of the returned lazy list
            * @param computingFunction to invoke whenever an element is first accessed
            *                          (may not return {@code null})
      diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java
      index eead46b9b7082..abee819069fc6 100644
      --- a/src/java.base/share/classes/java/util/Map.java
      +++ b/src/java.base/share/classes/java/util/Map.java
      @@ -1762,7 +1762,7 @@ static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
            * The provided computing function is guaranteed to be successfully invoked
            * at most once per key, even in a multi-threaded environment. Competing
            * threads accessing a value already under computation will block until an element
      -     * is computed or an exception is thrown by the computing thread.
      +     * is computed or the computing function completes abnormally.
            * <p>
            * If invoking the provided computing function throws an exception, it
            * is rethrown to the initial caller and no value associated with the provided key
      @@ -1774,26 +1774,29 @@ static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
            * cannot contain {@code null} values. Clients that want to use nullable values can
            * wrap values into an {@linkplain Optional} holder.
            * <p>
      -     * Any {@link Map#values()} or {@link Map#entrySet()} views of the returned map are
      -     * also lazily computed.
      +     * The values of any {@link Map#values()} or {@link Map#entrySet()} views of
      +     * the returned map are also lazily computed.
            * <p>
            * If the provided computing function recursively calls itself or
            * the returned lazy map for the same key, an {@linkplain IllegalStateException}
            * will be thrown.
            * <p>
      -     * The returned map's {@linkplain Object#equals(Object) equals()} and
      -     * {@linkplain Object#hashCode() hashCode()} methods may trigger initialization of one
      -     * or more lazy values. The returned maps's {@linkplain Object#toString() toString()}
      -     * method never triggers initialization of values. Instead, an
      -     * implementation-dependent string is returned for uninitialized values.
      +     * The returned map's {@linkplain Object Object methods};
      +     * {@linkplain Object#equals(Object) equals()},
      +     * {@linkplain Object#hashCode() hashCode()}, and
      +     * {@linkplain Object#toString() toString()} methods may trigger initialization of
      +     * one or more lazy elements.
            * <p>
            * The returned lazy map strongly references its underlying
      -     * computing function used to compute values only so long as there are
      -     * uncomputed values after which the underlying function is not strongly referenced
      -     * anymore and may be collected.
      +     * computing function used to compute values at least so long as there are
      +     * uncomputed values.
            * <p>
            * The returned Map is <em>not</em> {@linkplain Serializable}.
            *
      +     * @implNote  after all values have been initialized successfully, the computing
      +     *            function is no longer strongly referenced and becomes eligible for
      +     *            garbage collection.
      +     *
            * @param keys              the (non-null) keys in the returned computed map
            * @param computingFunction to invoke whenever an associated value is first accessed
            * @param <K>               the type of keys maintained by the returned map
      @@ -1811,7 +1814,8 @@ static <K, V> Map<K, V> ofLazy(Set<? extends K> keys,
               // Also, implicit null check of `keys` and all its elements
               final Set<K> keyCopies = Set.copyOf(keys);
               Objects.requireNonNull(computingFunction);
      -        if (keys instanceof EnumSet<?> && !keys.isEmpty()) {
      +        // We need to check the instance type using the original `keys` parameter.
      +        if (keys instanceof EnumSet<?> && !keyCopies.isEmpty()) {
                   @SuppressWarnings("unchecked")
                   var enumMap = (Map<K, V>) LazyCollections.ofLazyMapWithEnumKeys(keyCopies, computingFunction);
                   return enumMap;
      

            Assignee:
            Per-Ake Minborg
            Reporter:
            Per-Ake Minborg
            Maurizio Cimadamore
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: