mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Fix nullness mismatch for Converter/ConverterFactory
In gh-35947, the `Converter` contract was refined to allow for nullable return values. This created a mismatch with the `ConverterFactory` contract. This commit fixes this mismatch by allowing nullable return values in `Converter` instances created by `ConverterFactory`. Fixes gh-36063
This commit is contained in:
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.core.convert.converter;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A factory for "ranged" converters that can convert objects from S to subtypes of R.
|
||||
*
|
||||
@@ -36,6 +38,6 @@ public interface ConverterFactory<S, R> {
|
||||
* @param targetType the target type to convert to
|
||||
* @return a converter from S to T
|
||||
*/
|
||||
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
|
||||
<T extends R> Converter<S, @Nullable T> getConverter(Class<T> targetType);
|
||||
|
||||
}
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterFactory;
|
||||
import org.springframework.util.NumberUtils;
|
||||
@@ -41,7 +43,7 @@ import org.springframework.util.NumberUtils;
|
||||
final class CharacterToNumberFactory implements ConverterFactory<Character, Number> {
|
||||
|
||||
@Override
|
||||
public <T extends Number> Converter<Character, T> getConverter(Class<T> targetType) {
|
||||
public <T extends Number> Converter<Character, @Nullable T> getConverter(Class<T> targetType) {
|
||||
return new CharacterToNumber<>(targetType);
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterFactory;
|
||||
|
||||
@@ -30,7 +32,7 @@ import org.springframework.core.convert.converter.ConverterFactory;
|
||||
final class IntegerToEnumConverterFactory implements ConverterFactory<Integer, Enum> {
|
||||
|
||||
@Override
|
||||
public <T extends Enum> Converter<Integer, T> getConverter(Class<T> targetType) {
|
||||
public <T extends Enum> Converter<Integer, @Nullable T> getConverter(Class<T> targetType) {
|
||||
return new IntegerToEnum(ConversionUtils.getEnumType(targetType));
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalConverter;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -43,7 +45,7 @@ import org.springframework.util.NumberUtils;
|
||||
final class NumberToNumberConverterFactory implements ConverterFactory<Number, Number>, ConditionalConverter {
|
||||
|
||||
@Override
|
||||
public <T extends Number> Converter<Number, T> getConverter(Class<T> targetType) {
|
||||
public <T extends Number> Converter<Number, @Nullable T> getConverter(Class<T> targetType) {
|
||||
return new NumberToNumber<>(targetType);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import org.springframework.core.convert.converter.ConverterFactory;
|
||||
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
|
||||
|
||||
@Override
|
||||
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
|
||||
public <T extends Enum> Converter<String, @Nullable T> getConverter(Class<T> targetType) {
|
||||
return new StringToEnum(ConversionUtils.getEnumType(targetType));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ import org.springframework.util.NumberUtils;
|
||||
final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
|
||||
|
||||
@Override
|
||||
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
|
||||
public <T extends Number> Converter<String, @Nullable T> getConverter(Class<T> targetType) {
|
||||
return new StringToNumber<>(targetType);
|
||||
}
|
||||
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2025-present 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.convert.converter
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
/**
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class ConverterFactoryNullnessTests {
|
||||
|
||||
@Test
|
||||
fun converterFactoryWithNullableTypes() {
|
||||
val factory = StringToIdConverterFactory
|
||||
|
||||
val userIdConverter = factory.getConverter(UserId::class.java)
|
||||
assertThat(userIdConverter.convert("42")).isEqualTo(UserId("42"))
|
||||
}
|
||||
|
||||
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 val value: String
|
||||
}
|
||||
|
||||
data class UserId(override val value: String) : Id()
|
||||
|
||||
data class ProductId(override val value: String) : Id()
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user