I advise you to update your language knowledge to Java 24, C# 13, Scala 3.
From another comment of mine,
type Exp =
UnMinus of Exp
| Plus of Exp * Exp
| Minus of Exp * Exp
| Times of Exp * Exp
| Divides of Exp * Exp
| Power of Exp * Exp
| Real of float
| Var of string
| FunCall of string * Exp
| Fix of string * Exp
;;
Into the Java ADTs that you say Java doesn't have for sure,
public sealed interface Exp permits UnMinus, Plus, Minus, Times, Divides, Power, Real, Var, FunCall, Fix {}
public record UnMinus(Exp exp) implements Exp {}
public record Plus(Exp left, Exp right) implements Exp {}
public record Minus(Exp left, Exp right) implements Exp {}
public record Times(Exp left, Exp right) implements Exp {}
public record Divides(Exp left, Exp right) implements Exp {}
public record Power(Exp base, Exp exponent) implements Exp {}
public record Real(double value) implements Exp {}
public record Var(String name) implements Exp {}
public record FunCall(String functionName, Exp argument) implements Exp {}
public record Fix(String name, Exp argument) implements Exp {}
And a typical ML style evaluator, just for the kicks,
public class Evaluator {
public double eval(Exp exp) {
return switch (exp) {
case UnMinus u -> -eval(u.exp());
case Plus p -> eval(p.left()) + eval(p.right());
case Minus m -> eval(m.left()) - eval(m.right());
case Times t -> eval(t.left()) * eval(t.right());
case Divides d -> eval(d.left()) / eval(d.right());
case Power p -> Math.pow(eval(p.base()), eval(p.exponent()));
case Real r -> r.value();
case Var v -> context.valueOf(v.name);
case FunCall f -> eval(funcTable.get(f.functionName), f.argument);
case Fix fx -> eval(context.valueOf(v.name), f.argument);
};
}
}