In a Java program that I need to enhance I have a data structure
List<ImmutablePair<Integer, String>> params;
using ImmutablePair
from org.apache.commons.lang3.tuple
.
Going forward the String parameter needs to be able to hold either a String
or a Double
. For this I would like to create something like a union or Sum type of the two basic types.
How do I define this in Java for a list of ImmutablePair
s?
I'm not clear what your question has to do with ImmutablePair
.
I would just do something like this:
import java.util.function.Function;
public interface StringOrDouble {
<T> T get(Function<String,T> stringFunc, Function<Double,T> doubleFunc);
class StringVersion implements StringOrDouble {
private final String string;
public StringVersion(String string) {
this.string = string;
}
@Override
public <T> T get(Function<String, T> stringFunc, Function<Double, T> doubleFunc) {
return stringFunc.apply(string);
}
}
class DoubleVersion implements StringOrDouble {
private final Double doubleValue;
public DoubleVersion(Double doubleValue) {
this.doubleValue = doubleValue;
}
@Override
public <T> T get(Function<String, T> stringFunc, Function<Double, T> doubleFunc) {
return doubleFunc.apply(doubleValue);
}
}
static StringOrDouble ofString(String string) {
return new StringVersion(string);
}
static StringOrDouble ofDouble(Double doubleValue) {
return new DoubleVersion(doubleValue);
}
public static void main(String[] args) {
StringOrDouble[] vs = {ofString("hello"),ofDouble(101.0)};
for (StringOrDouble v : vs) {
System.out.println(v.get(String::toUpperCase, Double::toHexString));
}
}
}
And then use:
List<ImmutablePair<Integer, StringOrDouble>> params;
Or a generic version:
import java.util.List;
import java.util.function.Function;
public interface Union<A, B> {
<T> T get(Function<A, T> aFunc, Function<B, T> bFunc);
class AVersion<A, B> implements Union<A, B> {
private final A a;
public AVersion(A a) {
this.a = a;
}
@Override
public <T> T get(Function<A, T> aFunc, Function<B, T> bFunc) {
return aFunc.apply(a);
}
}
class BVersion<A, B> implements Union<A, B> {
private final B b;
public BVersion(B b) {
this.b = b;
}
@Override
public <T> T get(Function<A, T> aFunc, Function<B, T> bFunc) {
return bFunc.apply(b);
}
}
static <A, B> Union<A, B> ofA(A a) {
return new AVersion<>(a);
}
static <A, B> Union<A, B> ofB(B b) {
return new BVersion<>(b);
}
public static void main(String[] args) {
List<Union<String, Double>> vs = List.of(ofA("hello"), ofB(101.0));
for (Union<String, Double> v : vs) {
System.out.println(v.get(String::toUpperCase, Double::toHexString));
}
}
}
We can also add a method to Union
:
default void use(Consumer<A> aConsumer, Consumer<B> bConsumer) {
get(a -> {aConsumer.accept(a); return null;}, b -> {bConsumer.accept(b); return null;});
}
Which allows us to pass void methods, so if we have a bean like:
class Foo {
private String s;
private double d;
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
public double getD() {
return d;
}
public void setD(double d) {
this.d = d;
}
}
we can do:
Union<String, Double> u = ofA("a string");
Foo f = new Foo();
u.use(f::setS, f::setD);
And the appropriate setter will be called.