Browse Source

Make nullability flexible in ConverterFactory#getConverter

This commits udpates ConverterFactory#getConverter to accept both
nullable and non-null T in the Converter return value.

Flexible nullability at ConverterFactory type level would have been
ideal, but is not possible due to how Kotlin deals with Class<T> when
T is nullable.

See gh-36063
pull/36089/head
Sébastien Deleuze 1 month ago
parent
commit
96eee8c8b1
  1. 2
      spring-core/src/main/java/org/springframework/core/convert/converter/ConverterFactory.java
  2. 24
      spring-core/src/test/kotlin/org/springframework/core/convert/converter/ConverterFactoryNullnessTests.kt

2
spring-core/src/main/java/org/springframework/core/convert/converter/ConverterFactory.java

@ -38,6 +38,6 @@ public interface ConverterFactory<S, R> {
* @param targetType the target type to convert to * @param targetType the target type to convert to
* @return a converter from S to T * @return a converter from S to T
*/ */
<T extends R> Converter<S, @Nullable T> getConverter(Class<T> targetType); <T extends R> Converter<S, ? extends @Nullable T> getConverter(Class<T> targetType);
} }

24
spring-core/src/test/kotlin/org/springframework/core/convert/converter/ConverterFactoryNullnessTests.kt

@ -22,18 +22,27 @@ import kotlin.reflect.full.primaryConstructor
/** /**
* @author Brian Clozel * @author Brian Clozel
* @author Sebastien Deleuze
*/ */
class ConverterFactoryNullnessTests { class ConverterFactoryNullnessTests {
@Test @Test
fun converterFactoryWithNullableTypes() { fun converterFactoryWithNullableTypes() {
val factory = StringToNullableIdConverterFactory
val userIdConverter: Converter<String, UserId?> = factory.getConverter(UserId::class.java)
assertThat(userIdConverter.convert("42")).isEqualTo(UserId("42"))
}
@Test
fun converterFactoryWithNonNullableTypes() {
val factory = StringToIdConverterFactory val factory = StringToIdConverterFactory
val userIdConverter = factory.getConverter(UserId::class.java) val userIdConverter: Converter<String, UserId> = factory.getConverter(UserId::class.java)
assertThat(userIdConverter.convert("42")).isEqualTo(UserId("42")) assertThat(userIdConverter.convert("42")).isEqualTo(UserId("42"))
} }
object StringToIdConverterFactory : ConverterFactory<String, Id> { object StringToNullableIdConverterFactory : ConverterFactory<String, Id> {
override fun <T : Id> getConverter(targetType: Class<T>): Converter<String, T?> { override fun <T : Id> getConverter(targetType: Class<T>): Converter<String, T?> {
val constructor = checkNotNull(targetType.kotlin.primaryConstructor) val constructor = checkNotNull(targetType.kotlin.primaryConstructor)
return Converter { source -> return Converter { source ->
@ -42,12 +51,19 @@ class ConverterFactoryNullnessTests {
} }
} }
object StringToIdConverterFactory : ConverterFactory<String, Id> {
override fun <T : Id> getConverter(targetType: Class<T>): Converter<String, T> {
val constructor = checkNotNull(targetType.kotlin.primaryConstructor)
return Converter { source ->
constructor.call(source)
}
}
}
abstract class Id { abstract class Id {
abstract val value: String abstract val value: String
} }
data class UserId(override val value: String) : Id() data class UserId(override val value: String) : Id()
data class ProductId(override val value: String) : Id()
} }
Loading…
Cancel
Save