This little library makes it easy to create and use sum types (a.k.a. tagged unions) in Java 8+, similar to Swift’s enums with associated values.
This project aims to provide:
- A way to define a sum type with as little boilerplate code as possible.
- Ways to use the values of sum types that are both convenient and idiomatic in Java.
- Type safety, and other safety features.
- Extend the
Sum<This>
class, whereThis
is a self-reference to your own class. - Add a private constructor that takes a
Case
and anObject[]
as arguments, calling the superclass constructor. - Add a constant for each case of your sum type, of type
Case0
,Case1
, ...Case9
, depending on the number of associated values it needs. The constant value is always a method reference to your private constructor.
public final class Shape extends Sum<Shape>
{
public static final Case0<Shape> POINT = Shape::new;
public static final Case2<Shape, Double, Double> RECTANGLE = Shape::new;
public static final Case1<Shape, Double> CIRCLE = Shape::new;
private Shape(final Case<Shape> c, final Object[] vals) { super(c, vals); }
}
You use a sum-type value similarly to how you would use an Optional
value. First, use the init
method to create an instance:
final Shape shape = Shape.RECTANGLE.init(3.0, 5.5);
Then, to test the value of a sum type variable:
shape.ifCase(Shape.CIRCLE, radius -> graphics.drawCircle(radius));
If you need to return a value:
final double area1 = shape
.mapCase(Shape.RECTANGLE, (width, height) -> width * height)
.mapCase(Shape.CIRCLE, (radius) -> Math.PI * radius * radius)
.orElse(0.0);
To ensure all cases are covered:
final double area2 = shape
.mapCase(Shape.POINT, () -> 0.0)
.mapCase(Shape.CIRCLE, (radius) -> Math.PI * radius * radius)
.mapCase(Shape.RECTANGLE, (width, height) -> width * height)
.orElseThrow();
While this library gives a reasonable approximation of sum types, some limitations are worth noting.
- The associated values are anonymous. This can make things confusing. In the example above, you don't know whether
Shape.RECTANGLE
's associated values are(width, height)
or(height, width)
. Javadoc on the case constants helps a bit, but the best strategy is to use wrapper classes for your value types, and give them meaningful names. - The maximum number of associated values per case is nine (
Case0
~Case9
). It would be possible to create more, but I have to draw the line somewhere, and keeping it single-digit helps with IDE code completion. - Java's compiler errors can be confusing when you get the number or type of associated values wrong. When in doubt, check the declaration of the case constant.
- The
toString()
implementation uses reflection to get the name of the case.