I'm writing an interpreter, and I'm designing the abstract syntax tree currently. Right now, I'm trying to figure out a way to add (or do other operations with) two `Number`s, without casting to double or using a long switch statement

I've currently implemented addition with a switch statement, but I don't want to copy this for every operator. Is there a better way to do this?

**AdditionNumNum.java**

```
@Override
public ValueNumeric simplify(Context c) {
    ValueNumeric left = this.left.simplify(c);
    ValueNumeric right = this.right.simplify(c);

    NumberType type = NumberType.leastCommonType(left.getType(), right.getType());

    Number leftNum = left.get();
    Number rightNum = right.get();

    switch(type) {
    case BYTE:
        return new ValueNumeric(leftNum.byteValue() + rightNum.byteValue(), type);
    case SHORT:
        return new ValueNumeric(leftNum.shortValue() + rightNum.shortValue(), type);
    case INT:
        return new ValueNumeric(leftNum.intValue() + rightNum.intValue(), type);
    case LONG:
        return new ValueNumeric(leftNum.longValue() + rightNum.longValue(), type);
    case FLOAT:
        return new ValueNumeric(leftNum.floatValue() + rightNum.floatValue(), type);
    case DOUBLE:
        return new ValueNumeric(leftNum.doubleValue() + rightNum.doubleValue(), type);
    }

    throw new InterpreterException("Unknown type " + type);
}

```

**ValueNumeric.java**

```
public class ValueNumeric extends Value<Number> {
    private final Number value;
    private final NumberType type;

    public ValueNumeric(Number value) {
        this(value, NumberType.forClass(value.getClass()));
    }
    public ValueNumeric(Number value, NumberType type) {
        if (!value.getClass().equals(type.clazz)) {
            throw new IllegalArgumentException("Value (" + value
                    + ") is not of the right type for " + type + " ("
                    + value.getClass() + "/" + type.clazz + ")");
        }
        this.value = value;
        this.type = type;
    }
    @Override
    public Number get(){
        return value;
    }
    public NumberType getType() {
        return type;
    }
    public ValueNumeric castTo(NumberType newType) {
        if (newType == this.type) {
            return this;
        }
        Number newValue = type.cast(this.value);
        return new ValueNumeric(newValue, newType);
    }

    @Override
    public String toString() {
        return "ValueNumeric [value=" + value + ", type=" + type + "]";
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + ((type == null) ? 0 : type.hashCode());
        result = prime * result + ((value == null) ? 0 : value.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (!super.equals(obj))
            return false;
        if (getClass() != obj.getClass())
            return false;
        ValueNumeric other = (ValueNumeric) obj;
        if (type != other.type)
            return false;
        if (value == null) {
            if (other.value != null)
                return false;
        } else if (!value.equals(other.value))
            return false;
        return true;
    }

    public enum NumberType {
        BYTE(Byte.class, n -> Byte.valueOf(n.byteValue())),
        SHORT(Short.class, n -> Short.valueOf(n.shortValue())),
        INT(Integer.class, n -> Integer.valueOf(n.intValue())),
        LONG(Long.class, n -> Long.valueOf(n.longValue())),
        FLOAT(Float.class, n -> Float.valueOf(n.floatValue())),
        DOUBLE(Double.class, n -> Double.valueOf(n.doubleValue()));

        public final Class<? extends Number> clazz;
        private Function<Number, ? extends Number> castFunc;

        private <T extends Number> NumberType(Class<T> clazz,
                Function<Number, T> castFunc) {
            this.clazz = clazz;
            this.castFunc = castFunc;
        }

        public Number cast(Number number) {
            return castFunc.apply(number);
        }

        private static Map<Class<? extends Number>, NumberType> BY_CLASS = new HashMap<>();

        static {
            for (NumberType type : values()) {
                BY_CLASS.put(type.clazz, type);
            }
        }

        /**
         * 
         */
        public static NumberType forClass(Class<? extends Number> clazz) {
            return BY_CLASS.get(clazz);
        }

        /**
         * Returns the lowest common type between these two types of numbers,
         * for automatic type conversions.
         */
        public static NumberType leastCommonType(NumberType left, NumberType right) {
            if (left.ordinal() >= right.ordinal()) {
                return left;
            } else {
                return right;
            }
        }
    }
}

```

I would also appreciate any general comments on the code.