Skip to content

Expected

RichardHightower edited this page Apr 11, 2016 · 2 revisions

Expected is a similar concept to Optional in Java JDK and Option in Scala.

We added a new concept because this one is expected to come through callbacks and is used in places where Optional does not make sense. Also Reakt wants to use interfaces for all core concepts so others can provide their own implementations. In addition we wanted consumers for ifPresent and ifEmpty.

A Expected contains an value object which may or may not be set. This is like {@code Optional} but could be the value from an async operation. If a value is present, isPresent() will return true and get() will return the value. Expected is heavily modeled after optional (java.util.Optional).

Sample Usage

final Expected<Employee> empty = Expected.empty();
... //OR

final Expected<Employee> empty = Expected.ofNullable(null);
...


//isEmpty isPresent
assertTrue(   empty.isEmpty()    );
assertFalse(  empty.isPresent()  );

//ifEmpty ifPresent
empty.ifEmpty(() -> logger.info("Empty"));
empty.ifPresent(employee -> logger.info("Employee", employee));

//empty get throws a NoSuchElementException for empty expected
try {
            empty.get();
            reject();
} catch (NoSuchElementException nsee) {
}

Sample Usage non-empty

        final Expected<Employee> rick = Expected.ofNullable(new Employee("Rick"));
        ... //OR
        final Expected<Employee> rick = Expected.of(new Employee("Rick"));

        ...


       //ifEmpty ifPresent
       rick.ifEmpty(() -> logger.info("Empty"));
       rick.ifPresent(employee -> logger.info("Employee", employee));


       //Convert employee into a sheep using map
       final Sheep sheep = rick.map(employee -> new Sheep(employee.id)).get();
       assertEquals("Rick", sheep.id);


       //Use Filter
       assertTrue(
          rick.filter(employee -> employee.id.equals("Rick")
       ).isPresent()); //Rick is Rick
       assertFalse(
          rick.filter(employee -> employee.id.equals("Bob")
       ).isPresent()); //Rick is not Bob

Full example

package io.advantageous.reakt;

import org.junit.Test;

import java.util.NoSuchElementException;
import java.util.Optional;

import static org.junit.Assert.*;


public class ExpectedTest {

    @Test
    public void testEmpty() {

        final Expected<Employee> empty = Expected.empty();
        emptyTest(empty);

    }

    @Test
    public void testEmptyFromNull() {

        final Expected<Employee> empty = Expected.ofNullable(null);
        emptyTest(empty);

    }

    @Test
    public void testNotEmptyFromNullable() {

        final Expected<Employee> rick = Expected.ofNullable(new Employee("Rick"));
        notEmptyTest(rick);

    }

    @Test
    public void testNotEmpty() {

        final Expected<Employee> rick = Expected.of(new Employee("Rick"));
        notEmptyTest(rick);

    }

    @Test
    public void testNotEmptyFromOptional() {

        final Expected<Employee> rick = Expected.ofOptional(Optional.of(new Employee("Rick")));
        notEmptyTest(rick);

    }


    private void emptyTest(Expected<Employee> empty) {
        final boolean[] flag = new boolean[1];

        assertTrue(empty.isEmpty());
        assertFalse(empty.isPresent());

        /* Test ifEmpty and ifPresent. */
        empty.ifEmpty(() -> flag[0] = true);
        assertTrue(flag[0]);
        empty.ifPresent( employee -> flag[0] = false);
        assertTrue(flag[0]);

        final Employee bob = empty.orElse(new Employee("bob"));
        assertNotNull(bob);


        try {
            empty.get();
            reject();
        } catch (NoSuchElementException nsee) {

        }
        assertFalse(empty.filter(employee -> employee.id.equals("Rick")).isPresent());
        assertFalse(empty.filter(employee -> employee.id.equals("Bob")).isPresent());


        final Expected<Sheep> sheepValue = empty.map(employee -> new Sheep(employee.id));
        assertTrue(sheepValue.isEmpty());
    }


    private void notEmptyTest(Expected<Employee> rick) {
        final boolean[] flag = new boolean[1];

        assertFalse(rick.isEmpty());
        assertTrue(rick.isPresent());

        flag[0] = false;
        /* Test ifEmpty and ifPresent. */
        rick.ifEmpty(() -> flag[0] = true);
        assertFalse(flag[0]);
        rick.ifPresent((Employee employee) -> flag[0] = true);
        assertTrue(flag[0]);


        final Employee notBob = rick.orElse(new Employee("bob"));
        assertEquals("Rick", notBob.id);

        final Sheep sheep = rick.map(employee -> new Sheep(employee.id)).get();
        assertEquals("Rick", sheep.id);


        assertTrue(rick.filter(employee -> employee.id.equals("Rick")).isPresent());
        assertFalse(rick.filter(employee -> employee.id.equals("Bob")).isPresent());

        /** Test equals. */
        assertTrue(rick.equalsValue(new Employee("Rick")));
        assertTrue(rick.equals(rick));
        assertTrue(rick.equals(Expected.of(new Employee("Rick"))));
        assertFalse(rick.equals(new Employee("Rick")));
        rick.hashCode();
        rick.toString();
    }

    static class Employee {
        private final String id;

        Employee(String id) {
            this.id = id;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Employee employee = (Employee) o;

            return id != null ? id.equals(employee.id) : employee.id == null;

        }

        @Override
        public int hashCode() {
            return id != null ? id.hashCode() : 0;
        }
    }


    static class Sheep {
        private final String id;

        Sheep(String id) {
            this.id = id;
        }
    }

}

Expected

package io.advantageous.reakt;

import io.advantageous.reakt.impl.ExpectedImpl;

import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;


/**
 * Same concept as Optional in Java JDK and Option in Scala.
 * We added a new concept because this one is expected to come through callbacks and
 * is used in places where Optional does not make sense.
 * <p>
 * Also we want to use interfaces for all core concepts.
 * <p>
 * In addition we wanted callback for ifPresent and ifEmpty.
 * <p>
 * Contains an value object which may not be set. This is like {@code Optional} but could be the value from an async operation
 * which sent a null.
 * <p>
 * If a value is present, {@code isPresent()} will return {@code true} and
 * {@code get()} will return the value.
 * <p>
 * This is heavily modeled after {@link java.util.Optional} optional.
 */
public interface Expected<T> {
    /**
     * Common instance for {@code empty()}.
     */
    Expected EMPTY = new ExpectedImpl<>();

    /**
     * Returns an empty {@code ExpectedImpl} instance.  No value is present for this
     * value.
     *
     * @param <T> Type of the non-existent value
     * @return an empty {@code ExpectedImpl}
     */
    static <T> Expected<T> empty() {
        @SuppressWarnings("unchecked")
        Expected<T> t = EMPTY;
        return t;
    }

    /**
     * Returns an {@code ExpectedImpl} using the specified present value, which must not be null.
     *
     * @param <T>   the class of the value
     * @param value the value to be present. Must be non-null
     * @return an {@code ExpectedImpl} with the value present
     * @throws NullPointerException if value is null
     */
    static <T> Expected<T> of(T value) {
        return new ExpectedImpl<>(value);
    }

    /**
     * Returns an {@code ExpectedImpl} describing the specified value, if non-null,
     * otherwise returns an empty {@code ExpectedImpl}.
     *
     * @param <T>   the class of the value
     * @param value the possibly non-existent value
     * @return an {@code ExpectedImpl} with a present value if the specified value
     * is non-null, otherwise an empty {@code Optional}
     */
    static <T> Expected<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    /**
     * Returns an {@code ExpectedImpl} describing the specified value, if non-null,
     * otherwise returns an empty {@code ExpectedImpl}.
     *
     * @param <T>   the class of the value
     * @param value the possibly non-existent value
     * @return an {@code ExpectedImpl} with a present value if the specified value
     * is not empty, otherwise an empty {@code Optional}
     */
    static <T> Expected<T> ofOptional(Optional<T> value) {
        return !value.isPresent() ? empty() : of(value.get());
    }


    /**
     * If a value is present in this {@code Expected}, returns the value,
     * otherwise throws {@code NoSuchElementException}.
     *
     * @return the value held by this {@code Expected}
     * @throws NoSuchElementException if there is no value present
     * @see Expected#isPresent()
     */
    T get();


    /**
     * Return {@code true} if there is a value present, otherwise {@code false}.
     *
     * @return {@code true} if there is a value present, otherwise {@code false}
     */
    boolean isPresent();


    /**
     * Return {@code true} if there is not a value present, otherwise {@code false}.
     *
     * @return {@code true} if there is not a value present, otherwise {@code false}
     */
    boolean isEmpty();


    /**
     * If a value is present, invoke the consumer with the value.
     *
     * @param consumer executed if a value is present
     * @throws NullPointerException if value is present and {@code consumer} is
     *                              null
     */
    Expected<T> ifPresent(Consumer<? super T> consumer);


    /**
     * If a value is not present, invoke the runnable.
     *
     * @param runnable executed if a value is not present
     */
    Expected<T> ifEmpty(Runnable runnable);


    /**
     * If a value is present, and the value matches the given predicate,
     * return an {@code ExpectedImpl} describing the value, otherwise return an
     * empty {@code ExpectedImpl}.
     *
     * @param predicate a predicate to apply to the value, if present
     * @return an {@code ExpectedImpl} the value {@code Expected}
     * if present and the value matches the predicate,
     * otherwise an empty {@code Expected}
     * @throws NullPointerException if the predicate is null
     */
    Expected<T> filter(Predicate<? super T> predicate);


    /**
     * If a value present, use the mapping function to it,
     * and if the result is present, return an {@code ExpectedImpl} with the result.
     * Otherwise return an empty value {@code Expected}.
     *
     * @param <U>    The type of the result of the mapping function
     * @param mapper a mapper to apply to the value, if present
     * @return a value {@code Expected} which is the result of the mapper
     * function applied to {@code Expected} value if present or an empty value.
     * @throws NullPointerException if the mapper is null
     */
    <U> Expected<U> map(Function<? super T, ? extends U> mapper);


    /**
     * Return the value if present.  If not present return {@code other}.
     *
     * @param other value which is returned if no value present.
     * @return the value, if present, or if not present return {@code other}
     */
    T orElse(T other);


    /**
     * Indicates whether some other object is "equal to" the value.
     * The other value is equal if Object.equals(value, other) returns true.
     */
    boolean equalsValue(Object value);


}