Skip to content

Commit b2e94f6

Browse files
committed
Convert single null argument to Optional.empty() in SpEL varargs expression
Prior to this commit, if a single null value was passed to a method with a varargs array of type java.util.Optional, that null value was passed unmodified. On the contrary, a null passed with additional values to such a method resulted in the null being converted to Optional.empty(). This commit ensures that a single null value is also converted to Optional.empty() for such SpEL expressions. Closes gh-27795
1 parent ad7cdc5 commit b2e94f6

File tree

2 files changed

+20
-14
lines changed

2 files changed

+20
-14
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.lang.reflect.Executable;
2121
import java.lang.reflect.Method;
2222
import java.util.List;
23+
import java.util.Optional;
2324

2425
import org.springframework.core.MethodParameter;
2526
import org.springframework.core.convert.TypeDescriptor;
@@ -290,21 +291,29 @@ static boolean convertArguments(TypeConverter converter, Object[] arguments, Exe
290291
Object argument = arguments[varargsPosition];
291292
TypeDescriptor targetType = new TypeDescriptor(methodParam);
292293
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
293-
// If the argument is null or the argument type is equal to the varargs element type,
294-
// there is no need to convert it or wrap it in an array. For example, using
295-
// StringToArrayConverter to convert a String containing a comma would result in the
296-
// String being split and repackaged in an array when it should be used as-is.
297-
if (argument != null && !sourceType.equals(targetType.getElementTypeDescriptor())) {
294+
if (argument == null) {
295+
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
296+
if (targetType.getElementTypeDescriptor().getObjectType() == Optional.class) {
297+
arguments[varargsPosition] = Optional.empty();
298+
conversionOccurred = true;
299+
}
300+
}
301+
// If the argument type is equal to the varargs element type, there is no need to
302+
// convert it or wrap it in an array. For example, using StringToArrayConverter to
303+
// convert a String containing a comma would result in the String being split and
304+
// repackaged in an array when it should be used as-is.
305+
else if (!sourceType.equals(targetType.getElementTypeDescriptor())) {
298306
arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType);
299307
}
300-
// Possible outcomes of the above if-block:
308+
// Possible outcomes of the above if-else block:
301309
// 1) the input argument was null, and nothing was done.
302-
// 2) the input argument was correct type but not wrapped in an array, and nothing was done.
303-
// 3) the input argument was already compatible (i.e., array of valid type), and nothing was done.
304-
// 4) the input argument was the wrong type and got converted and wrapped in an array.
310+
// 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty().
311+
// 3) the input argument was correct type but not wrapped in an array, and nothing was done.
312+
// 4) the input argument was already compatible (i.e., array of valid type), and nothing was done.
313+
// 5) the input argument was the wrong type and got converted and wrapped in an array.
305314
if (argument != arguments[varargsPosition] &&
306315
!isFirstEntryInArray(argument, arguments[varargsPosition])) {
307-
conversionOccurred = true; // case 3
316+
conversionOccurred = true; // case 5
308317
}
309318
}
310319
// Otherwise, convert remaining arguments to the varargs element type.

spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,7 @@ public void testVarargsOptionalInvocation() {
302302
evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class);
303303
evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class);
304304
evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class);
305-
// The following should actually evaluate to [Optional.empty] instead of [null],
306-
// but ReflectionHelper.convertArguments() currently does not provide explicit
307-
// Optional support for a single argument passed to a varargs array.
308-
evaluate("optionalVarargsMethod(null)", "[null]", String.class);
305+
evaluate("optionalVarargsMethod(null)", "[Optional.empty]", String.class);
309306
evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class);
310307
evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class);
311308
}

0 commit comments

Comments
 (0)