
org.organicdesign.fp.oneOf.OneOf2OrNone Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Paguro Show documentation
Show all versions of Paguro Show documentation
Immutable Clojure collections and a Transformation abstraction for Java 8+, immutably, type-safely, and with good performance.
package org.organicdesign.fp.oneOf;
import org.organicdesign.fp.collections.ImList;
import org.organicdesign.fp.function.Fn0;
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 2 values or {@link None}. See {@link OneOf2} for a full description. If you're passing the same type,
you probably want a tuple instead.
*/
public abstract class OneOf2OrNone {
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 index 0 means this represents an A, 1 represents a B, 2 represents a None
*/
protected OneOf2OrNone(Object o, Class aClass, Class bClass, int index) {
types = RuntimeTypes.registerClasses(aClass, bClass, None.class);
sel = index;
item = o;
if (index < 0) {
throw new IllegalArgumentException("Selected item index must be 0-2");
} else if (index > 2) {
throw new IllegalArgumentException("Selected item index must be 0-2");
} else if ( (index == 2) && (o != null) ) {
throw new IllegalArgumentException("You specified the index " + index +
" meaning 'None' but passed a value: " + o);
}
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 fz the function to be executed if this OneOf stores a None.
@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,
Fn0 fz) {
if (sel == 0) {
return fa.apply((A) item);
} else if (sel == 1) {
return fb.apply((B) item);
} else {
return fz.apply();
}
}
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 OneOf2OrNone)) { return false; }
OneOf2OrNone that = (OneOf2OrNone) other;
return (sel == that.sel) &&
Objects.equals(item, that.item);
}
@Override public String toString() { return union2Str(sel == 2 ? None.NONE : item, types); }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy