diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index ad3b190f629..a44ce5898a7 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Predicate; import java.util.stream.Collectors; import kotlin.reflect.KFunction; @@ -759,17 +760,21 @@ public class MethodParameter { } else { KFunction function = null; + Predicate predicate = null; if (method != null) { function = ReflectJvmMapping.getKotlinFunction(method); + predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()); } else if (ctor != null) { function = ReflectJvmMapping.getKotlinFunction(ctor); + predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()) || + KParameter.Kind.INSTANCE.equals(p.getKind()); } if (function != null) { List parameters = function.getParameters(); KParameter parameter = parameters .stream() - .filter(p -> KParameter.Kind.VALUE.equals(p.getKind())) + .filter(predicate) .collect(Collectors.toList()) .get(index); return (parameter.getType().isMarkedNullable() || parameter.isOptional()); diff --git a/spring-core/src/test/kotlin/org/springframework/core/KotlinMethodParameterTests.kt b/spring-core/src/test/kotlin/org/springframework/core/KotlinMethodParameterTests.kt index 2157099a4ce..7580f254136 100644 --- a/spring-core/src/test/kotlin/org/springframework/core/KotlinMethodParameterTests.kt +++ b/spring-core/src/test/kotlin/org/springframework/core/KotlinMethodParameterTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.core -import java.lang.reflect.Method - -import org.junit.Before import org.junit.Test - import org.junit.Assert.* +import java.lang.reflect.Method /** * Tests for Kotlin support in [MethodParameter]. @@ -32,36 +28,58 @@ import org.junit.Assert.* */ class KotlinMethodParameterTests { - lateinit var nullableMethod: Method + private val nullableMethod: Method = javaClass.getMethod("nullable", String::class.java) - lateinit var nonNullableMethod: Method + private val nonNullableMethod = javaClass.getMethod("nonNullable", String::class.java) + private val innerClassConstructor = InnerClass::class.java.getConstructor(KotlinMethodParameterTests::class.java) - @Before - @Throws(NoSuchMethodException::class) - fun setup() { - nullableMethod = javaClass.getMethod("nullable", String::class.java) - nonNullableMethod = javaClass.getMethod("nonNullable", String::class.java) - } + private val innerClassWithParametersConstructor = InnerClassWithParameter::class.java + .getConstructor(KotlinMethodParameterTests::class.java, String::class.java, String::class.java) + + private val regularClassConstructor = RegularClass::class.java.getConstructor(String::class.java, String::class.java) @Test fun `Method parameter nullability`() { - assertTrue(MethodParameter(nullableMethod, 0).isOptional()) - assertFalse(MethodParameter(nonNullableMethod, 0).isOptional()) + assertTrue(MethodParameter(nullableMethod, 0).isOptional) + assertFalse(MethodParameter(nonNullableMethod, 0).isOptional) } @Test fun `Method return type nullability`() { - assertTrue(MethodParameter(nullableMethod, -1).isOptional()) - assertFalse(MethodParameter(nonNullableMethod, -1).isOptional()) + assertTrue(MethodParameter(nullableMethod, -1).isOptional) + assertFalse(MethodParameter(nonNullableMethod, -1).isOptional) + } + + @Test // SPR-17222 + fun `Inner class constructor`() { + assertFalse(MethodParameter(innerClassConstructor, 0).isOptional) + + assertFalse(MethodParameter(innerClassWithParametersConstructor, 0).isOptional) + assertFalse(MethodParameter(innerClassWithParametersConstructor, 1).isOptional) + assertTrue(MethodParameter(innerClassWithParametersConstructor, 2).isOptional) + } + + @Test + fun `Regular class constructor`() { + assertFalse(MethodParameter(regularClassConstructor, 0).isOptional) + assertTrue(MethodParameter(regularClassConstructor, 1).isOptional) } - @Suppress("unused", "unused_parameter") - fun nullable(p1: String?): Int? = 42 + @Suppress("unused_parameter") + fun nullable(nullable: String?): Int? = 42 + + @Suppress("unused_parameter") + fun nonNullable(nonNullable: String): Int = 42 + + inner class InnerClass + + @Suppress("unused_parameter") + inner class InnerClassWithParameter(nonNullable: String, nullable: String?) - @Suppress("unused", "unused_parameter") - fun nonNullable(p1: String): Int = 42 + @Suppress("unused_parameter") + class RegularClass(nonNullable: String, nullable: String?) } diff --git a/src/docs/asciidoc/languages/kotlin.adoc b/src/docs/asciidoc/languages/kotlin.adoc index 140742e40c0..f4d9667dc2b 100644 --- a/src/docs/asciidoc/languages/kotlin.adoc +++ b/src/docs/asciidoc/languages/kotlin.adoc @@ -143,6 +143,10 @@ for serializing / deserializing JSON data is automatically registered when found in the classpath and a warning message will be logged if Jackson and Kotlin are detected without the Jackson Kotlin module present. +Configuration classes can be +https://kotlinlang.org/docs/reference/nested-classes.html[top level or nested but not inner] +since the later requires a reference to the outer class. +