Like
Tuple2
, OneOf2 is designed to be sub-classed so you can add descriptive names.
Also because that's just the cleanest way for it to work in Java.
When Java users say "Pattern Matching" they mean Regular Expressions so the method that behaves
like what Scala and ML would call Pattern Matching is called "match" instead. The safest way
to use Union classes is to always call match() because it forces you to think about how to
handle each type you could possibly receive.
Usage:
thingy.match(fst -> fst.doOneThing(),
sec -> sec.doSomethingElse());
Sometimes it's a programming error to pass one type or another and you may want to throw an
exception.
oneOf.match(fst -> fst.doOneThing(),
sec -> { throw new IllegalStateException("Asked for a 2nd; only had a 1st."); });
As a shortcut, you can call (using method references) oneOf::throw1 or oneOf::throw2
oneOf.match(fst -> fst.doOneThing(),
oneOf::throw2);
For the shortest syntax and best names, define your own subclass. This is similar to sub-classing Tuples.
static class String_Integer extends OneOf2<String,Integer> {
// Ensure we use the one and only instance of this runtime types array to prevent duplicate array creation.
private static final ImList<Class> CLASS_STRING_INTEGER =
RuntimeTypes.registerClasses(vec(String.class, Integer.class));
// Constructor
private String_Integer(String s, Integer i, int n) { super(CLASS_STRING_INTEGER, s, i, n); }
// Static factory methods
public static String_Integer ofStr(String s) { return new String_Integer(s, null, 1); }
public static String_Integer ofInt(Integer i) { return new String_Integer(null, i, 2); }
// (Optional) "getter" methods that throw a detailed exception if the type isn't what you're expecting.
public String str() {
return super.match(s -> s,
super::throw2);
}
public Integer integer() {
return super.match(super::throw1,
i -> i);
}
}
OK, so that's kind of wordy, but it's Java, and you only have to do this once to presumably use it many times.
equals, hashcode, and toString are all taken care of for you.
Now you use descriptive and extremely brief syntax:
// Type-safe switching - always works at runtime.
x.match(s -> (s == null) ? null : s.lowerCase(),
n -> "This is the number " + n);
// If not a String at runtime throws "Expected a(n) String but found a(n) Integer"
x.str().contains("goody!");
// If not an Integer at runtime throws "Expected a(n) Integer but found a(n) String"
3 + x.integer();