# EvalEx: evaluating arithmetic expressions from strings, in Java, with BigDecimal throughout

> EvalEx is a dependency-free Java library that parses and evaluates expression strings, covering numbers, booleans, strings, dates, durations, arrays and structures, with BigDecimal arithmetic and pluggable functions and operators. Expressions can be copied so a single parsed syntax tree serves many threads, and the version to depend on is 3.7.0.

**ezylang/EvalEx** — EvalEx is a handy expression evaluator for Java, that allows to evaluate simple mathematical and boolean expressions.

- Repository: https://github.com/ezylang/EvalEx
- Website: https://ezylang.github.io/EvalEx/
- Stars: 1,119 · Forks: 293
- Language: Java
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/ezylang-evalex

## What the library does: parse an expression string and evaluate it to a typed value

EvalEx is described as a handy expression evaluator for Java that allows to parse and evaluate expression strings, and the one line description on Maven Central calls it a handy expression evaluator that allows to evaluate simple mathematical and boolean expressions. The result of an evaluation is an EvaluationValue rather than a raw Java object, and the type of that value follows from the expression. A numeric expression gives a number value, a comparison gives a boolean value, and a string concatenation gives a string value, and each is read out with the matching accessor such as getNumberValue, getBooleanValue or getStringValue. The scope of the language is broader than the short description suggests, covering numerical, boolean, string, date time, duration, array and structure expressions, operations and variables, with arrays and structures mixable so you can build arbitrary data structures inside an expression.

## The first example shows the shape of the API: construct, evaluate, read

The simplest example constructs an Expression from a string, evaluates it and reads the result. The expression in the README is 1 + 2 / (4 * SQRT(4)), which prints 1.25, and that single line tells you several things about the library. The arithmetic follows normal precedence with parentheses for grouping. The square root is a predefined mathematical function that is available without registration, which is a hint that the function library is broad rather than minimal. The result comes back as a single EvaluationValue and the caller picks the accessor that matches the expected type, so the type contract is explicit at the call site rather than implicit in a cast. The library is also designed to be constructed once and evaluated many times, since the constructor parses the string and the evaluate call runs it.

```java
Expression expression = new Expression("1 + 2 / (4 * SQRT(4))");

EvaluationValue result = expression.evaluate();

System.out.println(result.getNumberValue()); // prints 1.25
```

## Variables, and why a copy is what makes this thread safe

Variables are named in the expression and their values supplied at evaluation time with the with and and methods, and the example evaluates (a + b) * (a - b) with a set to 3.5 and b to 2.5 to produce 6.00. The interesting part is the next example, which is the reason to read the documentation rather than just the API. An expression can be copied, and the copy allows a thread-safe evaluation without parsing the expression again, using the same expression string, configuration and syntax tree. The instruction is explicit that each thread needs its own copy of the original expression. The example builds a + b, copies it, gives the copy different values, and prints 3 and 7 from the two. So parsing happens once and the tree is shared read-only, while the per-evaluation state lives in the copy. Skipping the copy is the obvious mistake here, and the result would be threads sharing evaluation state.

```java
Expression expression = new Expression("(a + b) * (a - b)");

EvaluationValue result = expression
    .with("a", 3.5)
    .and("b", 2.5)
    .evaluate();

System.out.println(result.getNumberValue()); // prints 6.00
```

```java
Expression expression = new Expression("a + b").with("a", 1).and("b", 2);
Expression copiedExpression = expression.copy().with("a", 3).and("b", 4);
```

## Data access: maps, a custom converter, and a data accessor interface

Values can be supplied one by one, or as a map passed to the withValues method, and the README shows a map with a boolean, a string and a decimal producing the string true : 24.7. Two extension points sit behind that. The data conversion of the passed values is performed automatically through a customizable converter, so how a Java object becomes an EvaluationValue is something you control rather than something fixed. And it is also possible to configure a custom data accessor to read and write values, which is the mechanism for evaluating an expression against an existing object graph rather than a bag of variables. The example for that is a price times quantity expression built with a configuration, validated with expression.validate(), and then evaluated from a copy constructed with a data accessor. The documentation points at a Data Types chapter for the conversion details and a Data Access chapter for the accessor, and those two chapters are where the real behaviour lives.

```java
Expression expression = new Expression("a+b+c");

Map<String, Object> values = new HashMap<>();
values.put("a", true);
values.put("b", " : ");
values.put("c", 24.7);

EvaluationValue result = expression.withValues(values).evaluate();

System.out.println(result.getStringValue()); // prints "true : 24.7"
```

## BigDecimal throughout, with configurable precision and optional rounding

The feature list states that numerical calculations use BigDecimal, and that MathContext and the number of decimal places can be configured, with optional automatic rounding. That is the design decision that distinguishes this library from a general purpose scripting engine, and it has a concrete consequence. A double based evaluator gives you a value whose exact decimal representation you cannot predict, which makes results that look wrong when a user reads them. With BigDecimal and a configurable MathContext, the arithmetic is defined precisely and the same expression on the same inputs gives the same digits, so an expression used in a pricing or regulatory calculation is auditable. The trade-off is speed, which the documentation does not claim anything about, and the fact that getting the scale and rounding right is now the caller's decision rather than a hidden default.

## The expression language is bigger than arithmetic: types, functions and syntax quirks

The language features beyond arithmetic are worth knowing before you commit. It supports the NULL datatype, hexadecimal and scientific notations of numbers, and implicit multiplication, so 2x, 2sin(x), (a+b)(a-b) and 2(x-y) all parse, with the last two written out as (a+b)*(a-b) and 2*(x-y) in the README's own notation. Function parameters are evaluated lazily, which the README ties to the IF function, and sub-expressions are supported. Functions can take a variable number of arguments, with MIN, MAX and SUM given as the examples. There are predefined boolean and mathematical operators, predefined mathematical, boolean and string functions, and custom functions and operators can be added. Strings and text mix the way they do in Java, so a quoted string can be concatenated with a variable holding a number, and the example produces Hello Frank, you are 38.

```java
Expression expression = new Expression("level > 2 || level <= 0");

EvaluationValue result = expression
    .with("level", 3.5)
    .evaluate();

System.out.println(result.getBooleanValue()); // prints true
```

## Arrays and structures: indexing and Java collections as the data model

Arrays, including multidimensional ones, can be passed as Java Lists or as instances of Java arrays, and the example indexes into two of them with a computed index, evaluating values[i-1] * factors[i-1] over a list of 2, 3, 4 and an array of 2, 4, 6 to produce 4. Structures are supported and can be passed as Java Maps, and arrays and structures can be mixed to build arbitrary data structures. The indexing syntax works on the collections you pass in rather than on a special array type, so the bridge between your Java objects and the expression language is the collection interface, which is why the two extension points for conversion and data access matter so much. A team with a domain model will find the data accessor route more useful than stuffing everything into a map, and the documentation's separate Data Access chapter is the place to work from.

## Packaging, licence and how to get it into a build

EvalEx is published to Maven Central under the group com.ezylang and the artifact EvalEx, where you will also find copy and paste templates for including it with build systems like Maven or Gradle. The README gives the Maven dependency block with the group, the artifact and version 3.7.0, and the Gradle equivalent. There are no external library dependencies, which for a library that will be evaluated in the middle of someone else's application is a genuine advantage, since adding it does not add a transitive dependency tree to audit. The minimum Java version is 11. The project carries a SonarCloud quality gate badge along with separate security rating and vulnerabilities badges, and the repository has a spotless/ directory, which is the code formatter configuration, plus a pom.xml, a docs/ directory for the documentation site, and AGENTS.md and CLAUDE.md at the root. The licence is Apache-2.0. The releases are 3.6.1 on 2026-04-18, 3.6.2 on 2026-06-04 and 3.7.0 on 2026-07-10, and the last push to the repository was on 2026-10-17.

## Conclusion

EvalEx suits a Java application that needs to let users or operators write rules, thresholds or report expressions as text and evaluate them safely against data it already holds, and that wants the evaluation to be reproducible across machines because every numeric operation uses BigDecimal. It is the wrong choice if you want to evaluate untrusted code, because the language includes function definitions and a data accessor, so the security model is the one you build around it rather than a sandbox it provides, and a poor fit if you want a rule engine with condition trees and side effects, since this is an expression evaluator, not a rule engine. Before adopting it, read the Data Types chapter for the conversion rules and the Data Access chapter for the accessor, and note that the library requires Java 11 or newer and publishes to Maven Central under the group com.ezylang.

## FAQ

### What is EvalEx and what does it evaluate?

EvalEx is a Java expression evaluator that parses and evaluates expression strings, supporting numerical, boolean, string, date time, duration, array and structure expressions, operations and variables. Results come back as an EvaluationValue read through getNumberValue, getBooleanValue, getStringValue and similar accessors.

### How do I add EvalEx to a Maven or Gradle project?

Both are published to Maven Central under the group com.ezylang and the artifact EvalEx. In Maven you add a dependency with groupId com.ezylang, artifactId EvalEx and version 3.7.0, and in Gradle you add compile 'com.ezylang:EvalEx:3.7.0' to the dependencies block.

### Is EvalEx thread safe?

An expression can be copied to allow a thread-safe evaluation of that copy without parsing the expression again, using the same expression string, configuration and syntax tree. The documentation says to make sure each thread has its own copy of the original expression.

### How does EvalEx handle decimal numbers?

Numerical calculations use BigDecimal, and the MathContext and the number of decimal places can be configured, with optional automatic rounding. There are also no dependencies to external libraries, and the minimum Java version is 11.

### How do I evaluate an expression against existing Java data?

You can pass values as a map to the withValues method, where conversion is done automatically through a customizable converter, or configure a custom data accessor to read and write values against an object graph. The documentation covers these in the Data Types and Data Access chapters.

## Sources

- [ezylang/EvalEx on GitHub](https://github.com/ezylang/EvalEx)
- [License: Apache-2.0](https://github.com/ezylang/EvalEx/blob/main/LICENSE)
- [Project website](https://ezylang.github.io/EvalEx/)
- [README](https://github.com/ezylang/EvalEx/blob/main/README.md)
- [Releases](https://github.com/ezylang/EvalEx/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ezylang-evalex
