changeset 7565:f971c0bb050e

Review feedback on Optional and unit test
author mduigou
date Tue, 05 Mar 2013 21:28:50 -0800
parents 98bf3c8a550c
children dbff2adcb22f
files src/share/classes/java/util/Optional.java test/java/util/Optional/Basic.java
diffstat 2 files changed, 214 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/Optional.java	Tue Mar 05 18:10:52 2013 +0100
+++ b/src/share/classes/java/util/Optional.java	Tue Mar 05 21:28:50 2013 -0800
@@ -36,7 +36,7 @@
  * value are provided, such as {@link #orElse(java.lang.Object) orElse()}
  * (return a default value if value not present) and
  * {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
- * of code if the value is present.)
+ * of code if the value is present).
  *
  * @since 1.8
  */
@@ -44,7 +44,7 @@
     /**
      * Common instance for {@code empty()}.
      */
-    private final static Optional<?> EMPTY = new Optional<>();
+    private static final Optional<?> EMPTY = new Optional<>();
 
     /**
      * If non-null, the value; if null, indicates no value is present
@@ -52,15 +52,6 @@
     private final T value;
 
     /**
-     * Construct an instance with a non-null value.
-     *
-     * @param value a non-null value.
-     */
-    private Optional(T value) {
-        this.value = Objects.requireNonNull(value);
-    }
-
-    /**
      * Construct an empty instance.
      *
      * @implNote generally only one empty instance, {@link Optional#EMPTY},
@@ -71,26 +62,37 @@
     }
 
     /**
-     * Return an empty {@code Optional}.  No value is present for this Optional.
+     * Returns an empty {@code Optional} instance.  No value is present for this
+     * Optional.
      *
      * @apiNote Though it may be tempting to do so, avoid testing if an object
-     * is empty by comparing with {@code ==} against instances returned
+     * is empty by comparing with {@code ==} against instances returned by
      * {@code Option.empty()}. There is no guarantee that it is a singleton.
-     * Instead, use {@code isPresent()}.
+     * Instead, use {@link #isPresent()}.
      *
-     * @param <T> Type of the non-existent value.
-     * @return an empty {@code Optional}.
+     * @param <T> Type of the non-existent value
+     * @return an empty {@code Optional}
      */
-    @SuppressWarnings("unchecked")
     public static<T> Optional<T> empty() {
-        return (Optional<T>) EMPTY;
+        @SuppressWarnings("unchecked")
+        Optional<T> t = (Optional<T>) EMPTY;
+        return t;
     }
 
     /**
-     * Create a new {@code Optional} with a present value.
+     * Construct an instance with a non-null value.
      *
-     * @param value The value, must be non-null.
-     * @param value new {@code Optional} with a present value.
+     * @param value a non-null value
+     */
+    private Optional(T value) {
+        this.value = Objects.requireNonNull(value);
+    }
+
+    /**
+     * Return an {@code Optional} with the specified present value.
+     *
+     * @param value the value to be present, which must be non-null
+     * @return an {@code Optional} with the value present
      */
     public static <T> Optional<T> of(T value) {
         return new Optional<>(value);
@@ -100,8 +102,8 @@
      * If a value is present in this {@code Optional}, returns the value,
      * otherwise throws {@code NoSuchElementException}.
      *
-     * @return the value held by this {@code Optional}.
-     * @throws NoSuchElementException if there is no value present.
+     * @return the non-null value held by this {@code Optional}
+     * @throws NoSuchElementException if there is no value present
      *
      * @see Optional#isPresent()
      */
@@ -115,19 +117,19 @@
     /**
      * Return {@code true} if there is a value present, otherwise {@code false}.
      *
-     * @return {@code true} if there is a value present, otherwise {@code false}.
+     * @return {@code true} if there is a value present, otherwise {@code false}
      */
     public boolean isPresent() {
         return value != null;
     }
 
     /**
-     * Execute the specified consumer with the value if a value is present,
+     * Have the specified consumer accept the value if a value is present,
      * otherwise do nothing.
      *
-     * @param consumer block to be executed if a value is present.
+     * @param consumer block to be executed if a value is present
      * @throws NullPointerException if value is present and {@code consumer} is
-     * null.
+     * null
      */
     public void ifPresent(Consumer<? super T> consumer) {
         if (value != null)
@@ -137,8 +139,9 @@
     /**
      * Return the value if present, otherwise return {@code other}.
      *
-     * @param other value to be returned if there is no value present.
-     * @return the value, if present, otherwise {@code other}.
+     * @param other the value to be returned if there is no value present, may
+     * be null
+     * @return the value, if present, otherwise {@code other}
      */
     public T orElse(T other) {
         return value != null ? value : other;
@@ -148,12 +151,13 @@
      * Return the value if present, otherwise invoke {@code other} and return
      * the result of that invocation.
      *
-     * @param other {@code Supplier} who's result is returned if there is no
-     * value present.
-     * @return the value if present otherwise return result of {@code other}.
-     * @throws NullPointerException if value is absent and {@code other} is null.
+     * @param other a {@code Supplier} whose result is returned if there is no
+     * value is present
+     * @return the value if present otherwise the result of {@code other.get()}
+     * @throws NullPointerException if value is not present and {@code other} is
+     * null
      */
-    public T orElseGet(Supplier<T> other) {
+    public T orElseGet(Supplier<? extends T> other) {
         return value != null ? value : other.get();
     }
 
@@ -161,15 +165,19 @@
      * Return the contained value, if present, otherwise throw an exception
      * to be created by the provided supplier.
      *
-     * @param <V> Type of the exception to be thrown.
+     * @apiNote A method reference to the exception constructor with an empty
+     * argument list can be used as the supplier. For example,
+     * {@code IllegalStateException::new}
+     *
+     * @param <X> Type of the exception to be thrown
      * @param exceptionSupplier The supplier which will return the exception to
-     * be thrown.
-     * @return the value.
-     * @throws V if there is no value present.
-     * @throws NullPointerException if value is absent and
-     * {@code exceptionSupplier} is null.
+     * be thrown
+     * @return the present value
+     * @throws X if there is no value present
+     * @throws NullPointerException if no value is present and
+     * {@code exceptionSupplier} is null
      */
-    public <V extends Throwable> T orElseThrow(Supplier<V> exceptionSupplier) throws V {
+    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
         if (value != null) {
             return value;
         } else {
@@ -177,25 +185,55 @@
         }
     }
 
+    /**
+     * Indicates whether some other object is "equal to" this Optional. The
+     * other object is considered equal if:
+     * <ul>
+     * <li>it is also an {@code Optional} and;
+     * <li>both instances have no value present or;
+     * <li>the present values are "equal to" each other via {@code equals()}.
+     * </ul>
+     *
+     * @param obj an object to be tested for equality
+     * @return {code true} if the other object is "equal to" this object
+     * otherwise {@code false}
+     */
     @Override
-    public boolean equals(Object o) {
-        if (this == o) {
+    public boolean equals(Object obj) {
+        if (this == obj) {
             return true;
         }
 
-        if (!(o instanceof Optional)) {
+        if (!(obj instanceof Optional)) {
             return false;
         }
 
-        Optional other = (Optional) o;
+        Optional other = (Optional) obj;
         return Objects.equals(value, other.value);
     }
 
+    /**
+     * Returns the hash code value of the present value, if any, or 0 (zero) if
+     * no value is present.
+     *
+     * @return hash code value of the present value or 0 if no value is present
+     */
     @Override
     public int hashCode() {
         return Objects.hashCode(value);
     }
 
+    /**
+     * Returns a non-empty string representation of this Optional suitable for
+     * debugging. The exact presentation format is unspecified and may vary
+     * between implementations and versions.
+     *
+     * @implSpec If a value is present the result must include its string
+     * representation in the result. Empty and present Optionals must be
+     * unambiguously identifiable.
+     *
+     * @return the string representation of this instance
+     */
     @Override
     public String toString() {
         return value != null
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/Optional/Basic.java	Tue Mar 05 21:28:50 2013 -0800
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1997, 2012, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary Basic functional test of Optional
+ * @author Mike Duigou
+ * @run testng Basic
+ */
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.testng.Assert.*;
+import org.testng.ITest;
+import org.testng.annotations.Factory;
+import org.testng.annotations.Test;
+
+
+public class Basic {
+
+    @Test(groups = "unit")
+    public void testEmpty() {
+        Optional<Boolean> empty = Optional.empty();
+        Optional<String> presentEmptyString = Optional.of("");
+        Optional<Boolean> present = Optional.of(Boolean.TRUE);
+
+        // empty
+        assertTrue(empty.equals(empty));
+        assertTrue(empty.equals(Optional.empty()));
+        assertTrue(!empty.equals(present));
+        assertTrue(0 == empty.hashCode());
+        assertTrue(!empty.toString().isEmpty());
+        assertTrue(!empty.toString().equals(presentEmptyString.toString()));
+        assertTrue(!empty.isPresent());
+        empty.ifPresent(v -> { fail(); });
+        assertSame(null, empty.orElse(null));
+        RuntimeException orElse = new RuntimeException() { };
+        assertSame(Boolean.FALSE, empty.orElse(Boolean.FALSE));
+        assertSame(null, empty.orElseGet(()-> null));
+        assertSame(Boolean.FALSE, empty.orElseGet(()-> Boolean.FALSE));
+    }
+
+        @Test(expectedExceptions=NoSuchElementException.class)
+        public void testEmptyGet() {
+            Optional<Boolean> empty = Optional.empty();
+
+            Boolean got = empty.get();
+        }
+
+        @Test(expectedExceptions=NullPointerException.class)
+        public void testEmptyOrElseGetNull() {
+            Optional<Boolean> empty = Optional.empty();
+
+            Boolean got = empty.orElseGet(null);
+        }
+
+        @Test(expectedExceptions=NullPointerException.class)
+        public void testEmptyOrElseThrowNull() throws Throwable {
+            Optional<Boolean> empty = Optional.empty();
+
+            Boolean got = empty.orElseThrow(null);
+        }
+
+        @Test(expectedExceptions=ObscureException.class)
+        public void testEmptyOrElseThrow() throws Exception {
+            Optional<Boolean> empty = Optional.empty();
+
+            Boolean got = empty.orElseThrow(ObscureException::new);
+        }
+
+        @Test(groups = "unit")
+        public void testPresent() {
+        Optional<Boolean> empty = Optional.empty();
+        Optional<String> presentEmptyString = Optional.of("");
+        Optional<Boolean> present = Optional.of(Boolean.TRUE);
+
+        // present
+        assertTrue(present.equals(present));
+        assertTrue(present.equals(Optional.of(Boolean.TRUE)));
+        assertTrue(!present.equals(empty));
+        assertTrue(Boolean.TRUE.hashCode() == present.hashCode());
+        assertTrue(!present.toString().isEmpty());
+        assertTrue(!present.toString().equals(presentEmptyString.toString()));
+        assertTrue(-1 != present.toString().indexOf(Boolean.TRUE.toString()));
+        assertSame(Boolean.TRUE, present.get());
+        try {
+            present.ifPresent(v -> { throw new ObscureException(); });
+            fail();
+        } catch(ObscureException expected) {
+
+        }
+        assertSame(Boolean.TRUE, present.orElse(null));
+        assertSame(Boolean.TRUE, present.orElse(Boolean.FALSE));
+        assertSame(Boolean.TRUE, present.orElseGet(null));
+        assertSame(Boolean.TRUE, present.orElseGet(()-> null));
+        assertSame(Boolean.TRUE, present.orElseGet(()-> Boolean.FALSE));
+        assertSame(Boolean.TRUE, present.<RuntimeException>orElseThrow( null));
+        assertSame(Boolean.TRUE, present.<RuntimeException>orElseThrow(ObscureException::new));
+    }
+
+    private static class ObscureException extends RuntimeException {
+
+    }
+}