Browse Source

Document support for overloading operators in SpEL in reference manual

Closes gh-32182
pull/32183/head
Sam Brannen 2 years ago
parent
commit
af2934c09b
  1. 71
      framework-docs/modules/ROOT/pages/core/expressions/language-ref/operators.adoc
  2. 38
      spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java

71
framework-docs/modules/ROOT/pages/core/expressions/language-ref/operators.adoc

@ -8,6 +8,8 @@ The Spring Expression Language supports the following kinds of operators: @@ -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-mathematical[Mathematical Operators]
* 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]]
@ -523,3 +525,72 @@ Kotlin:: @@ -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)
----
======

38
spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java

@ -32,6 +32,8 @@ import org.junit.jupiter.api.Test; @@ -32,6 +32,8 @@ import org.junit.jupiter.api.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
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.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
@ -447,6 +449,18 @@ class SpelDocumentationTests extends AbstractExpressionTests { @@ -447,6 +449,18 @@ class SpelDocumentationTests extends AbstractExpressionTests {
assertThat(parser.parseExpression("foo").getValue(context, inventor, String.class)).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
@ -672,4 +686,28 @@ class SpelDocumentationTests extends AbstractExpressionTests { @@ -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));
}
}
}

Loading…
Cancel
Save