文章目录
前言
在开发中,类型转换是我们天天都要做的事情.如何优雅的进行类型转换,而不是在逻辑代码中到处充斥着setter方法或者强制转换? 请使用Converter模式
提示:以下是本篇文章正文内容,下面案例可供参考
一、Converter关系图
二、代码
Converter抽象类
abstract class Converter<T, U> {
private Function<T,U> convertFunction;
public Converter(Function<T, U> convertFunction) {
this.convertFunction = convertFunction;
}
public U convert(T t){
return convertFunction.apply(t);
}
}
Converter类 保存了一个Function类实例: convertFunction, 利用convertFunction可以将一个对象T 转化为对象U.
Converter子类
- 我们这里假设要将User类,进行归档,保存为UserHistory类,看看User类是如何保存为UserHistory类的
public class User {
private String name;
private int mathScore;
private int englishScore;
public User(String name, int mathScore, int englishScore) {
this.name = name;
this.mathScore = mathScore;
this.englishScore = englishScore;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMathScore() {
return mathScore;
}
public void setMathScore(int mathScore) {
this.mathScore = mathScore;
}
public int getEnglishScore() {
return englishScore;
}
public void setEnglishScore(int englishScore) {
this.englishScore = englishScore;
}
}
public class UserHistory {
private String name;
private int totalScore;
private Date recordDate;
public UserHistory(String name, int totalScore, Date recordDate) {
this.name = name;
this.totalScore = totalScore;
this.recordDate = recordDate;
}
@Override
public String toString() {
return "UserHistory{" +
"name='" + name + '\'' +
", totalScore=" + totalScore +
", recordDate=" + recordDate +
'}';
}
}
public class UserToUserHistoryConverter extends Converter<User, UserHistory> {
public UserToUserHistoryConverter() {
super(UserToUserHistoryConverter::convertToHistory);
}
private static UserHistory convertToHistory(User user) {
return new UserHistory(user.getName(), user.getMathScore() + user.getEnglishScore(), new Date());
}
}
- 我们把具体的转换行为抽象到了convertToHistory方法中
具体使用
public static void main(String[] args) {
User user = new User("张三", 60, 70);
UserToUserHistoryConverter converter = new UserToUserHistoryConverter();
UserHistory history = converter.convert(user);
System.out.println(history);
}
输出:
UserHistory{name='张三', totalScore=130, recordDate=Wed Apr 21 16:07:49 CST 2021}
优点
- 转换行为抽离到了具体的Converter中去执行,简化了主逻辑代码
三、Spring中是如何使用Converter模式的
- 在spring-core包下的convert包下的类,大部分都是用来做转换用的,具体也是使用的Converter模式
spring 中的Converter类
/*
* Copyright 2002-2020 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.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* A converter converts a source object of type {@code S} to a target of type {@code T}.
* 从资源S 转换到 资源T
* <p>Implementations of this interface are thread-safe and can be shared.
*
* <p>Implementations may additionally implement {@link ConditionalConverter}.
*
* @author Keith Donald
* @author Josh Cummings
* @since 3.0
* @param <S> the source type
* @param <T> the target type
*/
@FunctionalInterface
public interface Converter<S, T> {
/**
* Convert the source object of type {@code S} to target type {@code T}.
* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
* @return the converted object, which must be an instance of {@code T} (potentially {@code null})
* @throws IllegalArgumentException if the source cannot be converted to the desired target type
*/
@Nullable
T convert(S source);
/**
* Construct a composed {@link Converter} that first applies this {@link Converter}
* to its input, and then applies the {@code after} {@link Converter} to the
* result.
* @param after the {@link Converter} to apply after this {@link Converter}
* is applied
* @param <U> the type of output of both the {@code after} {@link Converter}
* and the composed {@link Converter}
* @return a composed {@link Converter} that first applies this {@link Converter}
* and then applies the {@code after} {@link Converter}
* @since 5.3
*/
default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
Assert.notNull(after, "After Converter must not be null");
return (S s) -> {
T initialResult = convert(s);
return (initialResult != null ? after.convert(initialResult) : null);
};
}
}
- 在spring的Converter类中,使用了更加简便的方法
- 直接使用@FunctionalInterface注解将Converter声明为了一个lambda表达式,将S类实例转换为T实例
- 以及andThen 这个default方法为Converter类增加了链式调用的功能.
具体的实现类
我们看一个比较常用的方便理解: String 转 Boolean
/*
* Copyright 2002-2020 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.support;
import java.util.HashSet;
import java.util.Set;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
/**
* Converts String to a Boolean.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
*/
final class StringToBooleanConverter implements Converter<String, Boolean> {
private static final Set<String> trueValues = new HashSet<>(8);
private static final Set<String> falseValues = new HashSet<>(8);
static {
trueValues.add("true");
trueValues.add("on");
trueValues.add("yes");
trueValues.add("1");
falseValues.add("false");
falseValues.add("off");
falseValues.add("no");
falseValues.add("0");
}
@Override
@Nullable
public Boolean convert(String source) {
String value = source.trim();
if (value.isEmpty()) {
return null;
}
value = value.toLowerCase();
if (trueValues.contains(value)) {
return Boolean.TRUE;
}
else if (falseValues.contains(value)) {
return Boolean.FALSE;
}
else {
throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
}
}
}
- 具体代码一看就明白了,与map中的字符串相等就返回true,反之则为false;
spring Converter的其他扩展
- spring convert包下不单单只有简单的Converter转换,还有其他的扩展功能,包括
ConditionalConverter
:按条件转换,GenericConverter
通用的转换类,以及ConversionService
类: spring的大部分常用的转换都可以用ConversionService来进行操作,因为它默认提供了多个Converter
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
- 如果需要转换,只需要调用ConversionService的Convert方法即可
/**
* Convert the given {@code source} to the specified {@code targetType}.
* @param source the source object to convert (may be {@code null})
* @param targetType the target type to convert to (required)
* @return the converted object, an instance of targetType
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null}
*/
@Nullable
<T> T convert(@Nullable Object source, Class<T> targetType);
总结
Converter模式在类型转换方面有着广泛的应用,学习了Converter模式之后,希望自己能够不再主代码中写一大堆的setter方法了,也可以借鉴spring中的convert包,将常用转换写的MySelfBeanConvertService中来简化代码.