Document support for overloading operators in SpEL in reference manual
Closes gh-32182
This commit is contained in:
@@ -8,6 +8,8 @@ The Spring Expression Language supports the following kinds of operators:
|
|||||||
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-string[String Operators]
|
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-string[String Operators]
|
||||||
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-mathematical[Mathematical Operators]
|
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-mathematical[Mathematical Operators]
|
||||||
* xref:core/expressions/language-ref/operators.adoc#expressions-assignment[The Assignment Operator]
|
* xref:core/expressions/language-ref/operators.adoc#expressions-assignment[The Assignment Operator]
|
||||||
|
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-overloaded[Overloaded Operators]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[expressions-operators-relational]]
|
[[expressions-operators-relational]]
|
||||||
@@ -523,3 +525,72 @@ Kotlin::
|
|||||||
======
|
======
|
||||||
|
|
||||||
|
|
||||||
|
[[expressions-operators-overloaded]]
|
||||||
|
== Overloaded Operators
|
||||||
|
|
||||||
|
By default, the mathematical operations defined in SpEL's `Operation` enum (`ADD`,
|
||||||
|
`SUBTRACT`, `DIVIDE`, `MULTIPLY`, `MODULUS`, and `POWER`) support simple types like
|
||||||
|
numbers. By providing an implementation of `OperatorOverloader`, the expression language
|
||||||
|
can support these operations on other types.
|
||||||
|
|
||||||
|
For example, if we want to overload the `ADD` operator to allow two lists to be
|
||||||
|
concatenated using the `+` sign, we can implement a custom `OperatorOverloader` as
|
||||||
|
follows.
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
pubic class ListConcatenation implements OperatorOverloader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean overridesOperation(Operation operation, Object left, Object right) {
|
||||||
|
return (operation == Operation.ADD &&
|
||||||
|
left instanceof List && right instanceof List);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Object operate(Operation operation, Object left, Object right) {
|
||||||
|
if (operation == Operation.ADD &&
|
||||||
|
left instanceof List list1 && right instanceof List list2) {
|
||||||
|
|
||||||
|
List result = new ArrayList(list1);
|
||||||
|
result.addAll(list2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"No overload for operation %s and operands [%s] and [%s]"
|
||||||
|
.formatted(operation.name(), left, right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
If we register `ListConcatenation` as the `OperatorOverloader` in a
|
||||||
|
`StandardEvaluationContext`, we can then evaluate expressions like `{1, 2, 3} + {4, 5}`
|
||||||
|
as demonstrated in the following example.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
|
context.setOperatorOverloader(new ListConcatenation());
|
||||||
|
|
||||||
|
// evaluates to a new list: [1, 2, 3, 4, 5]
|
||||||
|
parser.parseExpression("{1, 2, 3} + {4, 5}").getValue(context, List.class);
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
StandardEvaluationContext context = StandardEvaluationContext()
|
||||||
|
context.setOperatorOverloader(ListConcatenation())
|
||||||
|
|
||||||
|
// evaluates to a new list: [1, 2, 3, 4, 5]
|
||||||
|
parser.parseExpression("{1, 2, 3} + {4, 5}").getValue(context, List::class.java)
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.ExpressionParser;
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.Operation;
|
||||||
|
import org.springframework.expression.OperatorOverloader;
|
||||||
import org.springframework.expression.common.TemplateParserContext;
|
import org.springframework.expression.common.TemplateParserContext;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
||||||
@@ -447,6 +449,18 @@ class SpelDocumentationTests extends AbstractExpressionTests {
|
|||||||
assertThat(parser.parseExpression("foo").getValue(context, inventor, String.class)).isEqualTo("Alexandar Seovic");
|
assertThat(parser.parseExpression("foo").getValue(context, inventor, String.class)).isEqualTo("Alexandar Seovic");
|
||||||
assertThat(aleks).isEqualTo("Alexandar Seovic");
|
assertThat(aleks).isEqualTo("Alexandar Seovic");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void overloadingOperators() {
|
||||||
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
|
context.setOperatorOverloader(new ListConcatenation());
|
||||||
|
|
||||||
|
// evaluates to [1, 2, 3, 4, 5]
|
||||||
|
List list = parser.parseExpression("{1, 2, 3} + {4, 5}").getValue(context, List.class);
|
||||||
|
assertThat(list).containsExactly(1, 2, 3, 4, 5);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@@ -672,4 +686,28 @@ class SpelDocumentationTests extends AbstractExpressionTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ListConcatenation implements OperatorOverloader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean overridesOperation(Operation operation, Object left, Object right) {
|
||||||
|
return (operation == Operation.ADD &&
|
||||||
|
left instanceof List && right instanceof List);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Object operate(Operation operation, Object left, Object right) {
|
||||||
|
if (operation == Operation.ADD &&
|
||||||
|
left instanceof List list1 && right instanceof List list2) {
|
||||||
|
|
||||||
|
List result = new ArrayList(list1);
|
||||||
|
result.addAll(list2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"No overload for operation %s and operands [%s] and [%s]"
|
||||||
|
.formatted(operation.name(), left, right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user