[[expressions-ref-functions]]
= Functions

You can extend SpEL by registering user-defined functions that can be called within
expressions by using the `#functionName(...)` syntax, and like with standard method
invocations, xref:core/expressions/language-ref/varargs.adoc[varargs] are also supported
for function invocations.

Functions can be registered as _variables_ in `EvaluationContext` implementations via the
`setVariable()` method.

[TIP]
====
`StandardEvaluationContext` also defines `registerFunction(...)` methods that provide a
convenient way to register a function as a `java.lang.reflect.Method` or a
`java.lang.invoke.MethodHandle`.
====

[WARNING]
====
Since functions share a common namespace with
xref:core/expressions/language-ref/variables.adoc[variables] in the evaluation context,
care must be taken to ensure that function names and variable names do not overlap.
====

The following example shows how to register a user-defined function to be invoked via
reflection using a `java.lang.reflect.Method`:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	Method method = ...;

	EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
	context.setVariable("myFunction", method);
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	val method: Method = ...

	val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
	context.setVariable("myFunction", method)
----
======

For example, consider the following utility method that reverses a string:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public abstract class StringUtils {

		public static String reverseString(String input) {
			return new StringBuilder(input).reverse().toString();
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	fun reverseString(input: String): String {
		return StringBuilder(input).reverse().toString()
	}
----
======

You can register and use the preceding method, as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	ExpressionParser parser = new SpelExpressionParser();

	EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
	context.setVariable("reverseString",
			StringUtils.class.getMethod("reverseString", String.class));

	// evaluates to "olleh"
	String helloWorldReversed = parser.parseExpression(
			"#reverseString('hello')").getValue(context, String.class);
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	val parser = SpelExpressionParser()

	val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
	context.setVariable("reverseString", ::reverseString.javaMethod)

	// evaluates to "olleh"
	val helloWorldReversed = parser.parseExpression(
			"#reverseString('hello')").getValue(context, String::class.java)
----
======

A function can also be registered as a `java.lang.invoke.MethodHandle`. This enables
potentially more efficient use cases if the `MethodHandle` target and parameters have
been fully bound prior to registration; however, partially bound handles are also
supported.

Consider the `String#formatted(Object...)` instance method, which produces a message
according to a template and a variable number of arguments
(xref:core/expressions/language-ref/varargs.adoc[varargs]).

You can register and use the `formatted` method as a `MethodHandle`, as the following
example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	ExpressionParser parser = new SpelExpressionParser();
	EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

	MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
			MethodType.methodType(String.class, Object[].class));
	context.setVariable("message", mh);

	// evaluates to "Simple message: <Hello World>"
	String message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
			.getValue(context, String.class);
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	val parser = SpelExpressionParser()
	val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

	val mh = MethodHandles.lookup().findVirtual(String::class.java, "formatted",
			MethodType.methodType(String::class.java, Array<Any>::class.java))
	context.setVariable("message", mh)

	// evaluates to "Simple message: <Hello World>"
	val message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
			.getValue(context, String::class.java)
----
======

As mentioned above, binding a `MethodHandle` and registering the bound `MethodHandle` is
also supported. This is likely to be more performant if both the target and all the
arguments are bound. In that case no arguments are necessary in the SpEL expression, as
the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	ExpressionParser parser = new SpelExpressionParser();
	EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

	String template = "This is a %s message with %s words: <%s>";
	Object varargs = new Object[] { "prerecorded", 3, "Oh Hello World!", "ignored" };
	MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
		MethodType.methodType(String.class, Object[].class))
			.bindTo(template)
			// Here we have to provide the arguments in a single array binding:
			.bindTo(varargs);
	context.setVariable("message", mh);

	// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
	String message = parser.parseExpression("#message()")
			.getValue(context, String.class);
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	val parser = SpelExpressionParser()
	val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

	val template = "This is a %s message with %s words: <%s>"
	val varargs = arrayOf("prerecorded", 3, "Oh Hello World!", "ignored")

	val mh = MethodHandles.lookup().findVirtual(String::class.java, "formatted",
		MethodType.methodType(String::class.java, Array<Any>::class.java))
			.bindTo(template)
			// Here we have to provide the arguments in a single array binding:
			.bindTo(varargs)
	context.setVariable("message", mh)

	// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
	val message = parser.parseExpression("#message()")
			.getValue(context, String::class.java)
----
======


