All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.organicdesign.fp.oneOf.OneOf4 Maven / Gradle / Ivy

package org.organicdesign.fp.oneOf;

import org.organicdesign.fp.collections.ImList;
import org.organicdesign.fp.function.Fn1;
import org.organicdesign.fp.type.RuntimeTypes;

import java.util.Objects;

import static org.organicdesign.fp.type.RuntimeTypes.union2Str;

/**
 Holds one of 4 values. See {@link OneOf2} for a full description.  If you're passing the same type, you probably
 want a tuple instead.
 */
public abstract class OneOf4 {
    private final Object item;
    private final int sel;
    private final ImList types;

    /**
     Protected constructor for subclassing.

     @param o the item (may be null)
     @param aClass class 0 (to have at runtime for descriptive error messages and toString()).
     @param bClass class 1 (to have at runtime for descriptive error messages and toString()).
     @param cClass class 2 (to have at runtime for descriptive error messages and toString()).
     @param dClass class 3 (to have at runtime for descriptive error messages and toString()).
     @param index 0 means this represents an A, 1 represents a B, 2 represents a C, 3 means D
     */
    protected OneOf4(Object o, Class aClass, Class bClass, Class cClass, Class dClass, int index) {
        types = RuntimeTypes.registerClasses(aClass, bClass, cClass, dClass);
        sel = index;
        item = o;
        if (index < 0) {
            throw new IllegalArgumentException("Selected item index must be 0-3");
        } else if (index > 3) {
            throw new IllegalArgumentException("Selected item index must be 0-3");
        }
        if ( (o != null) && (!types.get(index).isInstance(o)) ) {
            throw new ClassCastException("You specified index " + index + ", indicating a(n) " +
                                         types.get(index).getCanonicalName() + "," +
                                         " but passed a " + o.getClass().getCanonicalName());
        }
    }

    /**
     Languages that have union types built in have a match statement that works like this method.
     Exactly one of these functions will be executed - determined by which type of item this object holds.
     @param fa the function to be executed if this OneOf stores the first type.
     @param fb the function to be executed if this OneOf stores the second type.
     @param fc the function to be executed if this OneOf stores the third type.
     @param fd the function to be executed if this OneOf stores the fourth type.
     @return the return value of whichever function is executed.
     */
    // We only store one item and it's type is erased, so we have to cast it at runtime.
    // If sel is managed correctly, it ensures that the cast is accurate.
    @SuppressWarnings("unchecked")
    public  R match(Fn1 fa,
                       Fn1 fb,
                       Fn1 fc,
                       Fn1 fd) {
        if (sel == 0) {
            return fa.apply((A) item);
        } else if (sel == 1) {
            return fb.apply((B) item);
        } else if (sel == 2) {
            return fc.apply((C) item);
        } else {
            return fd.apply((D) item);
        }
    }

    public int hashCode() {
        // Simplest way to make the two items different.
        return Objects.hashCode(item) + sel;
    }

    @SuppressWarnings("unchecked")
    @Override public boolean equals(Object other) {
        if (this == other) { return true; }
        if (!(other instanceof OneOf4)) { return false; }

        OneOf4 that = (OneOf4) other;
        return (sel == that.sel) &&
               Objects.equals(item, that.item);
    }

    @Override public String toString() { return union2Str(item, types); }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy