Skip to content

Commit

Permalink
Refactor OptionalDouble
Browse files Browse the repository at this point in the history
  • Loading branch information
iselo committed Aug 24, 2023
1 parent 3ed845f commit 0a674e3
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 97 deletions.
228 changes: 131 additions & 97 deletions lib/src/main/java/co/raccoons/meeko/OptionalDouble.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
package co.raccoons.meeko;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.DoubleConsumer;
import java.util.function.DoubleSupplier;
import java.util.function.Supplier;
Expand All @@ -53,51 +54,116 @@
* use instances for synchronization, or unpredictable behavior may
* occur. For example, in a future release, synchronization may fail.
*
* @apiNote
* {@code OptionalDouble} is primarily intended for use as a method return type where
* @apiNote {@code OptionalDouble} is primarily intended for use as a method return type where
* there is a clear need to represent "no result." A variable whose type is
* {@code OptionalDouble} should never itself be {@code null}; it should always point
* to an {@code OptionalDouble} instance.
*
* @since 1.8
*/
public final class OptionalDouble {
/**
* Common instance for {@code empty()}.
*/
private static final OptionalDouble EMPTY = new OptionalDouble();
public class OptionalDouble {

/**
* If true then the value is present, otherwise indicates no value is present
*/
private final boolean isPresent;
private final double value;

/**
* Construct an empty instance.
*
* @implNote generally only one empty instance, {@link OptionalDouble#EMPTY},
* should exist per VM.
*/
private OptionalDouble() {
this.isPresent = false;
this.value = Double.NaN;
}

/**
* Returns an empty {@code OptionalDouble} instance. No value is present
* for this {@code OptionalDouble}.
*
* @apiNote
* Though it may be tempting to do so, avoid testing if an object is empty
* @return an empty {@code OptionalDouble}.
* @apiNote Though it may be tempting to do so, avoid testing if an object is empty
* by comparing with {@code ==} or {@code !=} against instances returned by
* {@code OptionalDouble.empty()}. There is no guarantee that it is a singleton.
* Instead, use {@link #isEmpty()} or {@link #isPresent()}.
*
* @return an empty {@code OptionalDouble}.
*/
public static OptionalDouble empty() {
return EMPTY;
return new OptionalDouble(Double.NaN) {

Check warning on line 78 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L78

Added line #L78 was not covered by tests

/**
* @inheritDoc
*/
@Override
public boolean isEmpty() {
return true;

Check warning on line 85 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L85

Added line #L85 was not covered by tests
}

/**
* @inheritDoc
*/
@Override
public boolean isPresent() {
return false;

Check warning on line 93 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L93

Added line #L93 was not covered by tests
}

/**
* @inheritDoc
*/
@Override
public double getAsDouble() {
throw new NoSuchElementException("No value present");

Check warning on line 101 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L101

Added line #L101 was not covered by tests
}

/**
* @inheritDoc
*/
@Override
public void ifPresent(DoubleConsumer action) {
// Intentionally empty
}

Check warning on line 110 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L110

Added line #L110 was not covered by tests

/**
* @inheritDoc
*/
@Override
public void ifPresentOrElse(DoubleConsumer action, Runnable emptyAction) {
Objects.requireNonNull(emptyAction);
emptyAction.run();
}

Check warning on line 119 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L117-L119

Added lines #L117 - L119 were not covered by tests

/**
* @inheritDoc
*/
@Override
public DoubleStream stream() {
return DoubleStream.empty();

Check warning on line 126 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L126

Added line #L126 was not covered by tests
}

/**
* @inheritDoc
*/
@Override
public double orElse(double other) {
return other;

Check warning on line 134 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L134

Added line #L134 was not covered by tests
}

/**
* @inheritDoc
*/
@Override
public double orElseGet(DoubleSupplier supplier) {
Objects.requireNonNull(supplier);
return supplier.getAsDouble();

Check warning on line 143 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L142-L143

Added lines #L142 - L143 were not covered by tests
}

/**
* @inheritDoc
*/
public <X extends Throwable> double orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
Objects.requireNonNull(exceptionSupplier);
throw exceptionSupplier.get();

Check warning on line 151 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L150-L151

Added lines #L150 - L151 were not covered by tests
}

/**
* @inheritDoc
*/
@Override
public int hashCode() {
return 0;

Check warning on line 159 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L159

Added line #L159 was not covered by tests
}

@Override
public String toString() {
return "OptionalDouble.empty";

Check warning on line 164 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L164

Added line #L164 was not covered by tests
}
};
}

/**
Expand All @@ -106,7 +172,6 @@ public static OptionalDouble empty() {
* @param value the double value to describe.
*/
private OptionalDouble(double value) {
this.isPresent = true;
this.value = value;
}

Check warning on line 176 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L174-L176

Added lines #L174 - L176 were not covered by tests

Expand All @@ -124,16 +189,11 @@ public static OptionalDouble of(double value) {
* If a value is present, returns the value, otherwise throws
* {@code NoSuchElementException}.
*
* @apiNote
* The preferred alternative to this method is {@link #orElseThrow()}.
*
* @return the value described by this {@code OptionalDouble}
* @throws NoSuchElementException if no value is present
* @apiNote The preferred alternative to this method is {@link #orElseThrow()}.
*/
public double getAsDouble() {
if (!isPresent) {
throw new NoSuchElementException("No value present");
}
return value;

Check warning on line 197 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L197

Added line #L197 was not covered by tests
}

Expand All @@ -143,18 +203,18 @@ public double getAsDouble() {
* @return {@code true} if a value is present, otherwise {@code false}
*/
public boolean isPresent() {
return isPresent;
return true;

Check warning on line 206 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L206

Added line #L206 was not covered by tests
}

/**
* If a value is not present, returns {@code true}, otherwise
* {@code false}.
*
* @return {@code true} if a value is not present, otherwise {@code false}
* @since 11
* @return {@code true} if a value is not present, otherwise {@code false}
* @since 11
*/
public boolean isEmpty() {
return !isPresent;
return false;

Check warning on line 217 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L217

Added line #L217 was not covered by tests
}

/**
Expand All @@ -163,56 +223,45 @@ public boolean isEmpty() {
*
* @param action the action to be performed, if a value is present
* @throws NullPointerException if value is present and the given action is
* {@code null}
* {@code null}
*/
public void ifPresent(DoubleConsumer action) {
if (isPresent) {
action.accept(value);
}
Objects.requireNonNull(action);
action.accept(value);
}

Check warning on line 231 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L229-L231

Added lines #L229 - L231 were not covered by tests

/**
* If a value is present, performs the given action with the value,
* otherwise performs the given empty-based action.
*
* @param action the action to be performed, if a value is present
* @param action the action to be performed, if a value is present
* @param emptyAction the empty-based action to be performed, if no value is
* present
* present
* @throws NullPointerException if a value is present and the given action
* is {@code null}, or no value is present and the given empty-based
* action is {@code null}.
* is {@code null}, or no value is present and the given empty-based
* action is {@code null}.
* @since 9
*/
public void ifPresentOrElse(DoubleConsumer action, Runnable emptyAction) {
if (isPresent) {
action.accept(value);
} else {
emptyAction.run();
}
ifPresent(action);
}

Check warning on line 247 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L246-L247

Added lines #L246 - L247 were not covered by tests

/**
* If a value is present, returns a sequential {@link DoubleStream}
* containing only that value, otherwise returns an empty
* {@code DoubleStream}.
*
* @apiNote
* This method can be used to transform a {@code Stream} of optional doubles
* @return the optional value as a {@code DoubleStream}
* @apiNote This method can be used to transform a {@code Stream} of optional doubles
* to a {@code DoubleStream} of present doubles:
* <pre>{@code
* Stream<OptionalDouble> os = ..
* DoubleStream s = os.flatMapToDouble(OptionalDouble::stream)
* }</pre>
*
* @return the optional value as a {@code DoubleStream}
* @since 9
*/
public DoubleStream stream() {
if (isPresent) {
return DoubleStream.of(value);
} else {
return DoubleStream.empty();
}
return DoubleStream.of(value);

Check warning on line 264 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L264

Added line #L264 was not covered by tests
}

/**
Expand All @@ -223,7 +272,7 @@ public DoubleStream stream() {
* @return the value, if present, otherwise {@code other}
*/
public double orElse(double other) {
return isPresent ? value : other;
return getAsDouble();

Check warning on line 275 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L275

Added line #L275 was not covered by tests
}

/**
Expand All @@ -232,12 +281,12 @@ public double orElse(double other) {
*
* @param supplier the supplying function that produces a value to be returned
* @return the value, if present, otherwise the result produced by the
* supplying function
* supplying function
* @throws NullPointerException if no value is present and the supplying
* function is {@code null}
* function is {@code null}
*/
public double orElseGet(DoubleSupplier supplier) {
return isPresent ? value : supplier.getAsDouble();
return getAsDouble();

Check warning on line 289 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L289

Added line #L289 was not covered by tests
}

/**
Expand All @@ -249,35 +298,26 @@ public double orElseGet(DoubleSupplier supplier) {
* @since 10
*/
public double orElseThrow() {
if (!isPresent) {
throw new NoSuchElementException("No value present");
}
return value;
return getAsDouble();

Check warning on line 301 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L301

Added line #L301 was not covered by tests
}

/**
* If a value is present, returns the value, otherwise throws an exception
* produced by the exception supplying function.
*
* @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 <X> Type of the exception to be thrown
* @param exceptionSupplier the supplying function that produces an
* exception to be thrown
* exception to be thrown
* @return the value, if present
* @throws X if no value is present
* @throws X if no value is present
* @throws NullPointerException if no value is present and the exception
* supplying function is {@code null}
* supplying function is {@code null}
* @apiNote A method reference to the exception constructor with an empty argument
* list can be used as the supplier. For example,
* {@code IllegalStateException::new}
*/
public<X extends Throwable> double orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (isPresent) {
return value;
} else {
throw exceptionSupplier.get();
}
public <X extends Throwable> double orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
return getAsDouble();

Check warning on line 320 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L320

Added line #L320 was not covered by tests
}

/**
Expand All @@ -292,7 +332,7 @@ public<X extends Throwable> double orElseThrow(Supplier<? extends X> exceptionSu
*
* @param obj an object to be tested for equality
* @return {@code true} if the other object is "equal to" this object
* otherwise {@code false}
* otherwise {@code false}
*/
@Override
public boolean equals(Object obj) {
Expand All @@ -301,39 +341,33 @@ public boolean equals(Object obj) {
}

return obj instanceof OptionalDouble other

Check warning on line 343 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L343

Added line #L343 was not covered by tests
&& (isPresent && other.isPresent
? Double.compare(value, other.value) == 0
: isPresent == other.isPresent);
&& (Double.compare(value, other.value) == 0);
}

/**
* Returns the hash code of the value, if present, otherwise {@code 0}
* (zero) if no value is present.
*
* @return hash code value of the present value or {@code 0} if no value is
* present
* present
*/
@Override
public int hashCode() {
return isPresent ? Double.hashCode(value) : 0;
return Double.hashCode(value);

Check warning on line 356 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L356

Added line #L356 was not covered by tests
}

/**
* Returns a non-empty string representation of this {@code OptionalDouble}
* 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
* @return the string representation of this instance
* @implSpec If a value is present the result must include its string representation
* in the result. Empty and present {@code OptionalDouble}s must be
* unambiguously differentiable.
*
* @return the string representation of this instance
*/
@Override
public String toString() {
return isPresent
? ("OptionalDouble[" + value + "]")
: "OptionalDouble.empty";
return ("OptionalDouble[" + value + "]");

Check warning on line 371 in lib/src/main/java/co/raccoons/meeko/OptionalDouble.java

View check run for this annotation

Codecov / codecov/patch

lib/src/main/java/co/raccoons/meeko/OptionalDouble.java#L371

Added line #L371 was not covered by tests
}
}
Loading

0 comments on commit 0a674e3

Please sign in to comment.