From d4c91d2fe048b5d2c6d6903f7e04dcf6aef18f0b Mon Sep 17 00:00:00 2001 From: sdeleuze Date: Thu, 11 Jan 2018 15:39:32 +0100 Subject: [PATCH] Improve Kotlin extensions doc about type erasure Since type erasure can be fixed only when using ParameterizedTypeReference based Java methods, TestRestTemplate API documentation should be updated to specify which extensions are subject to type erasure, and which are not. Closes gh-11604 --- .../web/client/TestRestTemplateExtensions.kt | 143 ++++++++++++------ .../client/TestRestTemplateExtensionsTests.kt | 30 ++++ 2 files changed, 123 insertions(+), 50 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/main/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensions.kt b/spring-boot-project/spring-boot-test/src/main/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensions.kt index 747fae85728..7ab8fd4ec46 100644 --- a/spring-boot-project/spring-boot-test/src/main/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensions.kt +++ b/spring-boot-project/spring-boot-test/src/main/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensions.kt @@ -25,8 +25,10 @@ import org.springframework.web.client.RestClientException import java.net.URI /** - * Extension for [TestRestTemplate.getForObject] avoiding specifying the type - * parameter thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.getForObject] providing a `getForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 @@ -36,8 +38,10 @@ inline fun TestRestTemplate.getForObject(url: String, vararg u getForObject(url, T::class.java, *uriVariables) /** - * Extension for [TestRestTemplate.getForObject] avoiding specifying the type - * parameter thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.getForObject] providing a `getForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 @@ -47,8 +51,10 @@ inline fun TestRestTemplate.getForObject(url: String, uriVaria getForObject(url, T::class.java, uriVariables) /** - * Extension for [TestRestTemplate.getForObject] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.getForObject] providing a `getForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 @@ -58,8 +64,10 @@ inline fun TestRestTemplate.getForObject(url: URI): T? = getForObject(url, T::class.java) /** - * Extension for [TestRestTemplate.getForEntity] avoiding requiring the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.getForEntity] providing a `getForEntity(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 @@ -69,8 +77,10 @@ inline fun TestRestTemplate.getForEntity(url: URI): ResponseEn getForEntity(url, T::class.java) /** - * Extension for [TestRestTemplate.getForEntity] avoiding requiring the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.getForEntity] providing a `getForEntity(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 @@ -80,8 +90,10 @@ inline fun TestRestTemplate.getForEntity(url: String, vararg u getForEntity(url, T::class.java, *uriVariables) /** - * Extension for [TestRestTemplate.getForEntity] avoiding requiring the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.getForEntity] providing a `getForEntity(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 @@ -91,140 +103,171 @@ inline fun TestRestTemplate.getForEntity(url: String, uriVaria getForEntity(url, T::class.java, uriVariables) /** - * Extension for [TestRestTemplate.patchForObject] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.patchForObject] providing a `patchForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.patchForObject(url: String, request: Any, vararg uriVariables: Any): T? = +inline fun TestRestTemplate.patchForObject(url: String, request: Any? = null, + vararg uriVariables: Any): T? = patchForObject(url, request, T::class.java, *uriVariables) /** - * Extension for [TestRestTemplate.patchForObject] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.patchForObject] providing a `patchForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.patchForObject(url: String, request: Any, uriVariables: Map): T? = +inline fun TestRestTemplate.patchForObject(url: String, request: Any? = null, + uriVariables: Map): T? = patchForObject(url, request, T::class.java, uriVariables) /** - * Extension for [TestRestTemplate.patchForObject] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.patchForObject] providing a `patchForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.patchForObject(url: URI, request: Any): T? = +inline fun TestRestTemplate.patchForObject(url: URI, request: Any? = null): T? = patchForObject(url, request, T::class.java) /** - * Extension for [TestRestTemplate.postForObject] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.postForObject] providing a `postForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.postForObject(url: String, request: Any, vararg uriVariables: Any): T? = +inline fun TestRestTemplate.postForObject(url: String, request: Any? = null, + vararg uriVariables: Any): T? = postForObject(url, request, T::class.java, *uriVariables) /** - * Extension for [TestRestTemplate.postForObject] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.postForObject] providing a `postForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.postForObject(url: String, request: Any, uriVariables: Map): T? = +inline fun TestRestTemplate.postForObject(url: String, request: Any? = null, + uriVariables: Map): T? = postForObject(url, request, T::class.java, uriVariables) /** - * Extension for [TestRestTemplate.postForObject] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.postForObject] providing a `postForObject(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.postForObject(url: URI, request: Any): T? = +inline fun TestRestTemplate.postForObject(url: URI, request: Any? = null): T? = postForObject(url, request, T::class.java) /** - * Extension for [TestRestTemplate.postForEntity] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.postForEntity] providing a `postForEntity(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.postForEntity(url: String, request: Any, vararg uriVariables: Any): ResponseEntity = +inline fun TestRestTemplate.postForEntity(url: String, request: Any? = null, + vararg uriVariables: Any): ResponseEntity = postForEntity(url, request, T::class.java, *uriVariables) /** - * Extension for [TestRestTemplate.postForEntity] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.postForEntity] providing a `postForEntity(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.postForEntity(url: String, request: Any, uriVariables: Map): ResponseEntity = +inline fun TestRestTemplate.postForEntity(url: String, request: Any? = null, + uriVariables: Map): ResponseEntity = postForEntity(url, request, T::class.java, uriVariables) /** - * Extension for [TestRestTemplate.postForEntity] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.postForEntity] providing a `postForEntity(...)` + * variant leveraging Kotlin reified type parameters. Like the original Java method, this + * extension is subject to type erasure. Use [exchange] if you need to retain actual + * generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.postForEntity(url: URI, request: Any): ResponseEntity = +inline fun TestRestTemplate.postForEntity(url: URI, request: Any? = null): ResponseEntity = postForEntity(url, request, T::class.java) /** - * Extension for [TestRestTemplate.exchange] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.exchange] providing an `exchange(...)` + * variant leveraging Kotlin reified type parameters. This extension is not subject to + * type erasure and retains actual generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.exchange(url: String, method: HttpMethod, requestEntity: HttpEntity<*>, vararg uriVariables: Any): ResponseEntity = +inline fun TestRestTemplate.exchange(url: String, method: HttpMethod, + requestEntity: HttpEntity<*>? = null, vararg uriVariables: Any): ResponseEntity = exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}, *uriVariables) /** - * Extension for [TestRestTemplate.exchange] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.exchange] providing an `exchange(...)` + * variant leveraging Kotlin reified type parameters. This extension is not subject to + * type erasure and retains actual generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.exchange(url: String, method: HttpMethod, requestEntity: HttpEntity<*>, uriVariables: Map): ResponseEntity = +inline fun TestRestTemplate.exchange(url: String, method: HttpMethod, + requestEntity: HttpEntity<*>? = null, uriVariables: Map): ResponseEntity = exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}, uriVariables) /** - * Extension for [TestRestTemplate.exchange] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.exchange] providing an `exchange(...)` + * variant leveraging Kotlin reified type parameters. This extension is not subject to + * type erasure and retains actual generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 */ @Throws(RestClientException::class) -inline fun TestRestTemplate.exchange(url: URI, method: HttpMethod, requestEntity: HttpEntity<*>): ResponseEntity = +inline fun TestRestTemplate.exchange(url: URI, method: HttpMethod, + requestEntity: HttpEntity<*>? = null): ResponseEntity = exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}) /** - * Extension for [TestRestTemplate.exchange] avoiding specifying the type parameter - * thanks to Kotlin reified type parameters. + * Extension for [TestRestTemplate.exchange] providing an `exchange(...)` + * variant leveraging Kotlin reified type parameters. This extension is not subject to + * type erasure and retains actual generic type arguments. * * @author Sebastien Deleuze * @since 2.0.0 diff --git a/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt b/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt index 8239f05d10f..bf3a45e3ceb 100644 --- a/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt +++ b/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt @@ -122,6 +122,13 @@ class TestRestTemplateExtensionsTests { verify(template, times(1)).patchForObject(url, body, Foo::class.java) } + @Test + fun `patchForObject with reified type parameters`() { + val url = "https://spring.io" + template.patchForObject(url) + verify(template, times(1)).patchForObject(url, null, Foo::class.java) + } + @Test fun `postForObject with reified type parameters, String, Any and varargs`() { val url = "https://spring.io" @@ -149,6 +156,13 @@ class TestRestTemplateExtensionsTests { verify(template, times(1)).postForObject(url, body, Foo::class.java) } + @Test + fun `postForObject with reified type parameters`() { + val url = "https://spring.io" + template.postForObject(url) + verify(template, times(1)).postForObject(url, null, Foo::class.java) + } + @Test fun `postForEntity with reified type parameters, String, Any and varargs`() { val url = "https://spring.io" @@ -176,6 +190,13 @@ class TestRestTemplateExtensionsTests { verify(template, times(1)).postForEntity(url, body, Foo::class.java) } + @Test + fun `postForEntity with reified type parameters`() { + val url = "https://spring.io" + template.postForEntity(url) + verify(template, times(1)).postForEntity(url, null, Foo::class.java) + } + @Test fun `exchange with reified type parameters, String, HttpMethod, HttpEntity and varargs`() { val url = "https://spring.io" @@ -217,6 +238,15 @@ class TestRestTemplateExtensionsTests { object : ParameterizedTypeReference>() {}) } + @Test + fun `exchange with reified type parameters, String and HttpMethod`() { + val url = "https://spring.io" + val method = HttpMethod.GET + template.exchange>(url, method) + verify(template, times(1)).exchange(url, method, null, + object : ParameterizedTypeReference>() {}) + } + @Test fun `RestOperations are available`() { val extensions = Class.forName(