diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java index d202a61a8c1..e9b62ec2adb 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java @@ -20,6 +20,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.util.List; +import java.util.Optional; import org.springframework.core.MethodParameter; import org.springframework.core.convert.TypeDescriptor; @@ -290,21 +291,29 @@ public abstract class ReflectionHelper { Object argument = arguments[varargsPosition]; TypeDescriptor targetType = new TypeDescriptor(methodParam); TypeDescriptor sourceType = TypeDescriptor.forObject(argument); - // If the argument is null or the argument type is equal to the varargs element type, - // there is no need to convert it or wrap it in an array. For example, using - // StringToArrayConverter to convert a String containing a comma would result in the - // String being split and repackaged in an array when it should be used as-is. - if (argument != null && !sourceType.equals(targetType.getElementTypeDescriptor())) { + if (argument == null) { + // Perform the equivalent of GenericConversionService.convertNullSource() for a single argument. + if (targetType.getElementTypeDescriptor().getObjectType() == Optional.class) { + arguments[varargsPosition] = Optional.empty(); + conversionOccurred = true; + } + } + // If the argument type is equal to the varargs element type, there is no need to + // convert it or wrap it in an array. For example, using StringToArrayConverter to + // convert a String containing a comma would result in the String being split and + // repackaged in an array when it should be used as-is. + else if (!sourceType.equals(targetType.getElementTypeDescriptor())) { arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType); } - // Possible outcomes of the above if-block: + // Possible outcomes of the above if-else block: // 1) the input argument was null, and nothing was done. - // 2) the input argument was correct type but not wrapped in an array, and nothing was done. - // 3) the input argument was already compatible (i.e., array of valid type), and nothing was done. - // 4) the input argument was the wrong type and got converted and wrapped in an array. + // 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty(). + // 3) the input argument was correct type but not wrapped in an array, and nothing was done. + // 4) the input argument was already compatible (i.e., array of valid type), and nothing was done. + // 5) the input argument was the wrong type and got converted and wrapped in an array. if (argument != arguments[varargsPosition] && !isFirstEntryInArray(argument, arguments[varargsPosition])) { - conversionOccurred = true; // case 3 + conversionOccurred = true; // case 5 } } // Otherwise, convert remaining arguments to the varargs element type. diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java index c003a07636b..6a94550104d 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java @@ -302,10 +302,7 @@ public class MethodInvocationTests extends AbstractExpressionTests { evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class); evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class); evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class); - // The following should actually evaluate to [Optional.empty] instead of [null], - // but ReflectionHelper.convertArguments() currently does not provide explicit - // Optional support for a single argument passed to a varargs array. - evaluate("optionalVarargsMethod(null)", "[null]", String.class); + evaluate("optionalVarargsMethod(null)", "[Optional.empty]", String.class); evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class); evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class); }