在针对page或者list接口的时候,需要转换对应的属性值是比较麻烦的。需要我们手动遍历对象,然后编写对应的转换方法。在多个转换的字段的时候,代码是非常长的。本工具可以一行实现字段转换。我个人觉得较为优雅。

初始版

import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.lang.reflect.Field;

/**
 * 字段转换工具类
 * 作者: zanglikun
 * 日期: 2024/7/19 上午11:16
 * 版权所有 © zanglikun.com
 */
public class ConvertFiledUtil {

    /**
     * 使用键值对映射更新对象列表中的字段
     *
     * @param list         目标列表
     * @param map          键值对映射 注意Key的类型
     * @param keyExtractor 提取键的函数 注意与Key的类型是否一致
     * @param valueSetter  设置值的函数
     * @param <T>          列表中对象的类型
     * @param <K>          键的类型
     * @param <V>          值的类型
     */
    public static <T, K, V> void updateListField(List<T> list, Map<K, V> map,
                                                 Function<T, K> keyExtractor, BiConsumer<T, V> valueSetter) {
        for (T item : list) {
            K key = keyExtractor.apply(item);
            if (map.containsKey(key)) {
                V value = map.get(key);
                valueSetter.accept(item, value);
            }
        }
    }

    /**
     * 使用反射更新对象列表中的字段
     *
     * @param list         目标列表
     * @param map          键值对映射
     * @param mapKeyField  用于映射的字段名
     * @param replaceField 要更新的字段名
     * @param <T>          列表中对象的类型
     * @param <K>          键的类型
     * @param <V>          值的类型
     */
    public static <T, K, V> void updateListField(List<T> list, Map<K, V> map, String mapKeyField, String replaceField) {
        for (T item : list) {
            try {
                // 通过反射获取 mapKeyField 字段的值
                Field keyField = item.getClass().getDeclaredField(mapKeyField);
                keyField.setAccessible(true);
                @SuppressWarnings("unchecked")
                K key = (K) keyField.get(item);

                // 从 map 中获取对应的值
                V value = map.get(key);

                // 通过反射设置 replaceField 字段的值
                Field valueField = item.getClass().getDeclaredField(replaceField);
                valueField.setAccessible(true);
                valueField.set(item, value);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

初始版使用代码演示

    @Override
    public List<AccountIpRecord> selectAccountIpRecordList(AccountIpRecord accountIpRecord) {
        // 1、获取账户IP记录列表
        List<AccountIpRecord> originList = accountIpRecordMapper.selectAccountIpRecordList(accountIpRecord);




        // 2.1、提取唯一的 charId
        List<Integer> charIds = originList.stream()
                .map(ele -> Integer.parseInt(ele.getCharid().toString())).distinct()
                .collect(Collectors.toList());

        // 2.2、获取角色名称数据
        List<MapKeyValue> charNameData = CollectionUtils.isEmpty(charIds)
                ? Collections.emptyList()
                : charsMapper.getCharNameByCharIds(charIds);

        // 2.3、将角色名称数据转换为映射
        Map<Long, String> charNameMap = charNameData.stream()
                .collect(Collectors.toMap(mapKeyValue -> Long.valueOf(mapKeyValue.getMapKey()), MapKeyValue::getMapValue));

        // 2.4、更新角色名称
        ConvertFiledUtil.updateListField(originList, charNameMap, AccountIpRecord::getCharid, AccountIpRecord::setCharName);






        // 3.1、提取唯一的客户端IP
        List<String> ipList = originList.stream().map(AccountIpRecord::getClientIp).distinct().collect(Collectors.toList());

        // 3.2、获取IP地区映射
        Map<String, String> ipRegionMap = Ip2AddressForJar.getContryCityHasFromatMap(ipList);

        // 3.3、更新客户端地址
        ConvertFiledUtil.updateListField(originList, ipRegionMap, AccountIpRecord::getClientIp, AccountIpRecord::setClientAddr);
        return originList;
    }

升级版优化

主要优化方向

1、定义线程安全的缓存Fileds,自主选择可选择减少反射性能消耗。

2、支持一次修改多个字段。(构造起来较为麻烦,不推荐)。

3、不想使用lambda,可直接使用字段名。

使用推荐:

import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 字段转换工具类 - 增强版
 * 提供多种方式进行对象字段的转换和更新,支持缓存、异常处理、类型安全等特性
 *
 * @author zanglikun
 * @version 2.1
 * @since 2024/7/19
 */
public final class ConvertFieldUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConvertFieldUtil.class);
    // Key 是类名+"." + 字段名,Value 是对应的 Field 对象
    private static final Map<String, Field> FIELD_CACHE = new ConcurrentHashMap<>();

    /**
     * 私有构造函数,防止实例化
     * 如果尝试实例化,将抛出 UnsupportedOperationException 异常。
     * 1. 这样可以避免不必要的内存开销。
     * 2. 也可以防止用户误用该类的实例化。
     * 3. 这种设计模式在 Java 中被广泛使用,符合最佳实践,有助于代码的可读性和可维护性。
     */
    private ConvertFieldUtil() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }

    // 操作结果统计
    public static class UpdateResult {
        private final int totalItems;
        private final int successCount;
        private final int failureCount;
        private final List<String> errors;

        public UpdateResult(int totalItems, int successCount, int failureCount, List<String> errors) {
            this.totalItems = totalItems;
            this.successCount = successCount;
            this.failureCount = failureCount;
            this.errors = Collections.unmodifiableList(new ArrayList<>(errors));
        }

        public int getTotalItems() {
            return totalItems;
        }

        public int getSuccessCount() {
            return successCount;
        }

        public int getFailureCount() {
            return failureCount;
        }

        public List<String> getErrors() {
            return errors;
        }

        public boolean hasErrors() {
            return failureCount > 0;
        }

        public String getErrorsAsString() {
            return String.join(System.lineSeparator(), errors);
        }

        @Override
        public String toString() {
            return String.format("UpdateResult{total=%d, success=%d, failure=%d, errors=%d}",
                    totalItems, successCount, failureCount, errors.size());
        }
    }

    /**
     * 自定义异常类
     */
    public static class FieldUpdateException extends RuntimeException {
        public FieldUpdateException(String message, Throwable cause) {
            super(message, cause);
        }
        public FieldUpdateException(String message) {
            super(message);
        }
    }


    /**
     * 使用函数式接口更新对象列表中的字段(推荐方式)
     *
     * @param list         目标列表,不能为null
     * @param map          键值对映射,不能为null
     * @param keyExtractor 提取键的函数,不能为null
     * @param valueSetter  设置值的函数,不能为null
     * @param <T>          列表中对象的类型
     * @param <K>          键的类型
     * @param <V>          值的类型
     * @return 更新结果统计
     * @throws FieldUpdateException 当参数为null时
     */
    public static <T, K, V> UpdateResult updateListWithFunction(
            List<T> list,
            Map<K, V> map,
            Function<T, K> keyExtractor,
            BiConsumer<T, V> valueSetter) {

        return updateListWithFunction(list, map, keyExtractor, valueSetter, null);
    }

    /**
     * 使用函数式接口更新对象列表中的字段,支持条件过滤
     *
     * @param list         目标列表,不能为null
     * @param map          键值对映射,不能为null
     * @param keyExtractor 提取键的函数,不能为null
     * @param valueSetter  设置值的函数,不能为null
     * @param filter       过滤条件,为null时不过滤
     * @param <T>          列表中对象的类型
     * @param <K>          键的类型
     * @param <V>          值的类型
     * @return 更新结果统计
     * @throws FieldUpdateException 当必要参数为null时
     */
    public static <T, K, V> UpdateResult updateListWithFunction(
            List<T> list,
            Map<K, V> map,
            Function<T, K> keyExtractor,
            BiConsumer<T, V> valueSetter,
            Predicate<T> filter) {

        // 参数验证
        validateNotNull(list, "list");
        validateNotNull(map, "map");
        validateNotNull(keyExtractor, "keyExtractor");
        validateNotNull(valueSetter, "valueSetter");

        if (list.isEmpty() || map.isEmpty()) {
            return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
        }

        final List<String> errors = Collections.synchronizedList(new ArrayList<>());
        final Predicate<T> finalFilter = filter != null ? filter : item -> true;

        long successCount = list.stream()
                .filter(finalFilter)
                .map(item -> {
                    try {
                        K key = keyExtractor.apply(item);
                        V value = (key != null) ? map.get(key) : null;
                        if (value != null) {
                            valueSetter.accept(item, value);
                            return true;
                        }
                        return false;
                    } catch (Exception e) {
                        String errorMsg = String.format("Failed to update item %s: %s", item, e.getMessage());
                        errors.add(errorMsg);
                        LOGGER.warn(errorMsg, e);
                        return false;
                    }
                })
                .filter(Boolean::booleanValue)
                .count();


        return new UpdateResult(list.size(), (int) successCount, errors.size(), errors);
    }


    /**
     * 使用反射更新对象列表中的字段(带缓存优化)
     *
     * @param list         目标列表,不能为null
     * @param map          键值对映射,不能为null
     * @param mapKeyField  用于映射的字段名,不能为null或空字符串
     * @param replaceField 要更新的字段名,不能为null或空字符串
     * @param <T>          列表中对象的类型
     * @param <K>          键的类型
     * @param <V>          值的类型
     * @return 更新结果统计
     * @throws FieldUpdateException 当参数无效时
     */
    public static <T, K, V> UpdateResult updateListWithReflection(
            List<T> list,
            Map<K, V> map,
            String mapKeyField,
            String replaceField) {

        // 参数验证
        validateNotNull(list, "list");
        validateNotNull(map, "map");
        validateNotNullOrEmpty(mapKeyField, "mapKeyField");
        validateNotNullOrEmpty(replaceField, "replaceField");

        if (list.isEmpty() || map.isEmpty()) {
            return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
        }

        final List<String> errors = Collections.synchronizedList(new ArrayList<>());

        long successCount = list.stream()
                .map(item -> {
                    try {
                        Field keyField = getFieldWithCache(item.getClass(), mapKeyField);
                        @SuppressWarnings("unchecked")
                        K key = (K) getFieldValue(keyField, item);

                        V value = (key != null) ? map.get(key) : null;
                        if (value != null) {
                            Field valueField = getFieldWithCache(item.getClass(), replaceField);
                            setFieldValue(valueField, item, value);
                            return true;
                        }
                        return false;
                    } catch (Exception e) {
                        String errorMsg = String.format("Failed to update item with key field '%s' and replace field '%s': %s",
                                mapKeyField, replaceField, e.getMessage());
                        errors.add(errorMsg);
                        LOGGER.warn(errorMsg, e);
                        return false;
                    }
                })
                .filter(Boolean::booleanValue)
                .count();

        return new UpdateResult(list.size(), (int) successCount, errors.size(), errors);
    }

    /**
     * 批量更新多个字段(此方法不建议直接调用,如需使用,可参考demo4)
     *
     * @param list           目标列表
     * @param fieldMappings  字段映射配置 Map<源字段名, 目标字段名>
     * @param dataMap        数据映射 Map<键值, Map<字段名, 字段值>>
     * @param keyField       键字段名
     * @param <T>            列表中对象的类型
     * @param <K>            键的类型
     * @return 更新结果统计
     */
    public static <T, K> UpdateResult updateMultipleFields(
            List<T> list,
            Map<String, String> fieldMappings,
            Map<K, Map<String, Object>> dataMap,
            String keyField) {

        validateNotNull(list, "list");
        validateNotNull(fieldMappings, "fieldMappings");
        validateNotNull(dataMap, "dataMap");
        validateNotNullOrEmpty(keyField, "keyField");

        if (list.isEmpty() || fieldMappings.isEmpty() || dataMap.isEmpty()) {
            return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
        }

        final List<String> errors = Collections.synchronizedList(new ArrayList<>());

        long successCount = list.stream()
                .map(item -> {
                    try {
                        Field keyFieldObj = getFieldWithCache(item.getClass(), keyField);
                        @SuppressWarnings("unchecked")
                        K key = (K) getFieldValue(keyFieldObj, item);

                        Map<String, Object> fieldValues = (key != null) ? dataMap.get(key) : null;
                        if (fieldValues == null) {
                            return false;
                        }

                        boolean updated = false;
                        for (Map.Entry<String, String> mapping : fieldMappings.entrySet()) {
                            String sourceField = mapping.getKey();
                            String targetField = mapping.getValue();

                            if (fieldValues.containsKey(sourceField)) {
                                Field targetFieldObj = getFieldWithCache(item.getClass(), targetField);
                                Object value = fieldValues.get(sourceField);
                                setFieldValue(targetFieldObj, item, value);
                                updated = true;
                            }
                        }
                        return updated;
                    } catch (Exception e) {
                        String errorMsg = String.format("Failed to update multiple fields for item: %s", e.getMessage());
                        errors.add(errorMsg);
                        LOGGER.warn(errorMsg, e);
                        return false;
                    }
                })
                .filter(Boolean::booleanValue)
                .count();

        return new UpdateResult(list.size(), (int) successCount, errors.size(), errors);
    }

    /**
     * 批量更新多个字段(直接映射版)
     * <p>
     * 此版本假定数据Map中的key直接对应目标对象的字段名,无需额外的映射关系。
     *
     * @param list     目标列表
     * @param dataMap  数据映射 Map<键值, Map<字段名, 字段值>>
     * @param keyField 键字段名
     * @param <T>      列表中对象的类型
     * @param <K>      键的类型
     * @return 更新结果统计
     */
    public static <T, K> UpdateResult updateMultipleFields(
            List<T> list,
            Map<K, Map<String, Object>> dataMap,
            String keyField) {

        validateNotNull(list, "list");
        validateNotNull(dataMap, "dataMap");
        validateNotNullOrEmpty(keyField, "keyField");

        if (list.isEmpty() || dataMap.isEmpty()) {
            return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
        }

        // 从dataMap的第一个非空value中提取所有key作为字段映射
        Map<String, String> fieldMappings = dataMap.values().stream()
                .filter(Objects::nonNull)
                .flatMap(m -> m.keySet().stream())
                .distinct()
                .collect(Collectors.toMap(Function.identity(), Function.identity()));

        if (fieldMappings.isEmpty()) {
            return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
        }

        return updateMultipleFields(list, fieldMappings, dataMap, keyField);
    }


    // ========== 私有辅助方法 ==========

    /**
     * 获取字段(带缓存),会递归查找父类字段
     */
    private static Field getFieldWithCache(Class<?> clazz, String fieldName) {
        String cacheKey = clazz.getName() + "." + fieldName;
        return FIELD_CACHE.computeIfAbsent(cacheKey, key -> {
            Class<?> current = clazz;
            while (current != null) {
                try {
                    Field field = current.getDeclaredField(fieldName);
                    field.setAccessible(true);
                    return field;
                } catch (NoSuchFieldException e) {
                    current = current.getSuperclass();
                }
            }
            throw new FieldUpdateException("Field not found: " + fieldName + " in class " + clazz.getName() + " and its superclasses.");
        });
    }

    /**
     * 获取字段值
     */
    private static Object getFieldValue(Field field, Object obj) throws IllegalAccessException {
        return field.get(obj);
    }

    /**
     * 设置字段值(带类型检查)
     */
    private static void setFieldValue(Field field, Object obj, Object value) throws IllegalAccessException {
        // 基本类型检查
        if (value != null && !ClassUtils.isAssignable(value.getClass(), field.getType())) {
            throw new IllegalArgumentException(
                    String.format("Cannot assign value of type %s to field %s of type %s",
                            value.getClass().getName(), field.getName(), field.getType().getName()));
        }
        field.set(obj, value);
    }

    /**
     * 验证参数不为null
     */
    private static void validateNotNull(Object obj, String paramName) {
        if (obj == null) {
            throw new FieldUpdateException("Parameter '" + paramName + "' cannot be null");
        }
    }

    /**
     * 验证字符串参数不为null或空
     */
    private static void validateNotNullOrEmpty(String str, String paramName) {
        if (StringUtils.isBlank(str)) {
            throw new FieldUpdateException("Parameter '" + paramName + "' cannot be null or empty");
        }
    }

    /**
     * 清空字段缓存(用于内存管理或测试场景)
     */
    public static void clearFieldCache() {
        FIELD_CACHE.clear();
        LOGGER.info("Field cache cleared.");
    }

    /**
     * 获取缓存统计信息
     */
    public static String getCacheStats() {
        return String.format("Field cache size: %d entries.", FIELD_CACHE.size());
    }
}

演示Demo

1、demoUpdateWithFunction(): 推荐的使用方式,类型安全且性能高

2、demoUpdateWithReflection():您无法或不想使用Lambda表达式,而是希望通过字段名称(字符串)来操作时使用。

3、demoUpdateMultipleFields():非常不推荐,构造参数非常麻烦!

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ConvertFieldUtil 工具类使用演示
 *
 * @author zanglikun
 * @version 1.0
 * @since 2025/07/24
 */
public class ConvertFieldUtilDemo {

    // 内部静态类,用于演示
    static class User {
        private final Long id;
        private final String username;
        private String role;
        private final Long deptId;
        private String deptName;

        public User(Long id, String username, String role, Long deptId) {
            this.id = id;
            this.username = username;
            this.role = role;
            this.deptId = deptId;
        }

        // Getters and Setters
        public Long getId() { return id; }
        public String getUsername() { return username; }
        public String getRole() { return role; }
        public Long getDeptId() { return deptId; }
        public String getDeptName() { return deptName; }
        public void setDeptName(String deptName) { this.deptName = deptName; }
        public void setRole(String role) { this.role = role; }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", role='" + role + '\'' +
                    ", deptId=" + deptId +
                    ", deptName='" + deptName + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        System.out.println("--- ConvertFieldUtil Demo ---");

        // --- 1. 使用函数式接口 updateListWithFunction ---
        demoUpdateWithFunction();

        // --- 2. 使用反射 updateListWithReflection ---
        demoUpdateWithReflection();

        // --- 3. 批量更新多个字段(简化版) demoUpdateMultipleFieldsSimplified ---
        demoUpdateMultipleFieldsSimplified();
    }

    private static List<User> createSampleUserList() {
        List<User> userList = new ArrayList<>();
        userList.add(new User(1L, "admin", "admin", 100L));
        userList.add(new User(2L, "ry", "developer", 101L));
        userList.add(new User(3L, "test", "tester", 102L));
        userList.add(new User(4L, "extra", "guest", 103L));
        return userList;
    }

    /**
     * 演示 updateListWithFunction
     */
    private static void demoUpdateWithFunction() {
        System.out.println("\n--- 1. demoUpdateWithFunction ---");
        List<User> userList = createSampleUserList();
        System.out.println("更新前: " + userList);

        // 准备一个部门ID到部门名称的映射
        Map<Long, String> deptMap = new HashMap<>();
        deptMap.put(100L, "总经办");
        deptMap.put(101L, "研发部门");
        // 注意:没有102L的映射,用于测试未匹配的情况

        // 调用工具类方法,通过部门ID (deptId) 来更新部门名称 (deptName)
        ConvertFieldUtil.UpdateResult result = ConvertFieldUtil.updateListWithFunction(
                userList,
                deptMap, // 第三个参数类型=Map的Key,第四个参数类型=Map的Value类型
                User::getDeptId, // keyExtractor: 使用方法引用从User对象中获取deptId
                User::setDeptName  // valueSetter: 使用方法引用将Map中的值设置到deptName字段
        );

        System.out.println("更新后: " + userList);
        System.out.println("更新结果: " + result);
    }

    /**
     * 演示 updateListWithReflection
     */
    private static void demoUpdateWithReflection() {
        System.out.println("\n--- 2. demoUpdateWithReflection ---");
        List<User> userList = createSampleUserList();
        System.out.println("更新前: " + userList);

        // 准备一个用户名到角色的映射
        Map<String, String> roleMap = new HashMap<>();
        roleMap.put("admin", "超级管理员");
        roleMap.put("ry", "普通用户");
        roleMap.put("non_exist", "不存在的用户"); // 用于测试未匹配

        // 调用工具类方法,通过反射,使用 username 字段匹配,更新 role 字段
        ConvertFieldUtil.UpdateResult result = ConvertFieldUtil.updateListWithReflection(
                userList,
                roleMap,
                "username", // mapKeyField: User对象中用于匹配Map Key的字段名
                "role"      // replaceField: User对象中需要被更新的字段名
        );

        System.out.println("更新后: " + userList);
        System.out.println("更新结果: " + result);
    }

    /**
     * 演示 updateMultipleFields
     */
    private static void demoUpdateMultipleFields() {
        System.out.println("\n--- 3. demoUpdateMultipleFields ---");
        List<User> userList = createSampleUserList();
        System.out.println("更新前: " + userList);

        // 准备一个更复杂的数据源,一个ID可以更新多个字段
        Map<Long, Map<String, Object>> dataMap = new HashMap<>();

        Map<String, Object> user1Data = new HashMap<>();
        user1Data.put("newRole", "系统管理员");
        user1Data.put("newDeptName", "总裁办");
        dataMap.put(1L, user1Data);

        Map<String, Object> user2Data = new HashMap<>();
        user2Data.put("newRole", "高级工程师");
        user2Data.put("newDeptName", "核心研发部");
        dataMap.put(2L, user2Data);

        // 定义数据源字段到目标对象字段的映射关系
        Map<String, String> fieldMappings = new HashMap<>();
        fieldMappings.put("newRole", "role");
        fieldMappings.put("newDeptName", "deptName");

        // 调用工具类方法,通过用户ID,批量更新 role 和 deptName
        ConvertFieldUtil.UpdateResult result = ConvertFieldUtil.updateMultipleFields(
                userList,
                fieldMappings,
                dataMap,
                "id" // keyField: User对象中用于匹配dataMap Key的字段名
        );

        System.out.println("更新后: " + userList);
        System.out.println("更新结果: " + result);
    }

    /**
     * 演示 updateMultipleFields 的简化版本
     */
    private static void demoUpdateMultipleFieldsSimplified() {
        System.out.println("\n--- 4. demoUpdateMultipleFieldsSimplified ---");
        List<User> userList = createSampleUserList();
        System.out.println("更新前: " + userList);

        // 准备数据源,这次Map的Key直接对应User对象的字段名
        Map<Long, Map<String, Object>> dataMap = new HashMap<>();

        Map<String, Object> user1Data = new HashMap<>();
        user1Data.put("role", "系统管理员"); // key "role" 直接对应 User.role
        user1Data.put("deptName", "总裁办"); // key "deptName" 直接对应 User.deptName
        dataMap.put(1L, user1Data);

        Map<String, Object> user2Data = new HashMap<>();
        user2Data.put("role", "高级工程师");
        user2Data.put("deptName", "核心研发部");
        dataMap.put(2L, user2Data);

        // 调用简化版的工具类方法,无需提供 fieldMappings
        ConvertFieldUtil.UpdateResult result = ConvertFieldUtil.updateMultipleFields(
                userList,
                dataMap,
                "id" // keyField: User对象中用于匹配dataMap Key的字段名
        );

        System.out.println("更新后: " + userList);
        System.out.println("更新结果: " + result);
    }
}

特殊说明:
上述文章均是作者实际操作后产出。烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤

免责声明:
本站文章旨在总结学习互联网技术过程中的经验与见解。任何人不得将其用于违法或违规活动!所有违规内容均由个人自行承担,与作者无关。