• 作者:老汪软件技巧
  • 发表时间:2024-09-30 04:00
  • 浏览量:

最近我在处理一个项目,遇到了一个需求:需要将一个类的属性转化为字符串列表的形式,比如某个类有属性 id、username、email,我要把这些属性转换成 ["id", "username", "email"] 这样的字符串数组。这个需求看似简单,但如何做到通用性,并应用到不同类的场景呢?

解决方案Java 的反射机制

要将一个类的属性名(字段名)提取出来,并将它们转换为 ["id", "username", "email"] 这种字符串表示,可以通过 反射 来实现。通过反射可以获取类中的所有字段,然后将字段名转换成字符串。

使用 Java 的 反射机制,通过 Class 对象获取类的所有字段。遍历这些字段,并将字段名转换为一个列表。将列表转换成特定格式(例如,["id", "username", "email"])。

代码实现如下所示:

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FieldNameUtil {
    // 通用方法:将类的字段名转换为 ["id", "username", "email"] 这种格式的字符串
    public static String getFieldNamesAsString(Class clazz) {
        // 获取类的所有字段
        Field[] fields = clazz.getDeclaredFields();
        
        // 提取字段名并转为List
        List fieldNames = Arrays.stream(fields)
                                        .map(Field::getName) // 获取字段名
                                        .collect(Collectors.toList());
        
        // 将List 转换为类似 ["id", "username", "email"] 的字符串
        return fieldNames.toString();
    }
    public static void main(String[] args) {
        // 假设有个User类
        class User {
            private Long id;
            private String username;
            private String email;
        }
        // 调用方法获取字段名的字符串表示
        String result = getFieldNamesAsString(User.class);
        System.out.println(result); // 输出: [id, username, email]
    }
}

代码解析反射获取字段:通过 clazz.getDeclaredFields() 获取类的所有字段,这会返回一个 Field[] 数组,包含类中的所有属性。映射字段名:使用 Arrays.stream(fields).map(Field::getName) 方法,将每个 Field 对象映射为它的字段名。转换为列表:使用 Collectors.toList() 将映射后的字段名收集为一个 List。生成字符串:fieldNames.toString() 会自动将 List 转换为特定的格式 ["id", "username", "email"]。

运行输出结果:

[id, username, email]

进一步优化:自定义字段名和忽略字段

在实际的应用场景中,这里不希望直接使用属性名作为输出,或者需要忽略某些字段。比如,我需要把 userName 转换成 user_name,而忽略掉 password 字段。为了解决这个问题,我们可以通过 Java 注解 来控制字段的转换方式。

为了解决这个问题,我设计了两个自定义注解:

反射字段并且赋值实例_反射获取字段名_

@AppRecordName:用于自定义某个字段的名称。@IgnoreAppRecord:用于忽略某个字段的转化。实现思路:

定义注解的代码如下所示:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 自定义注解:用于指定字段的别名
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AppRecordName {
    String value();
}
// 自定义注解:用于忽略字段
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface IgnoreAppRecord {
}

然后实现通用工具类的改造,通过这些注解,我们可以在工具类中增加逻辑,动态检测注解,从而实现更灵活的字段名转化。:

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class AppRecordUtil {
    // 通用方法:将类的字段名转换为 ["id", "username", "email"] 这种格式的字符串
    public static String getFieldNamesAsString(Class clazz) {
        // 获取类的所有字段
        Field[] fields = clazz.getDeclaredFields();
        
        // 提取字段名并转为List
        List fieldNames = Arrays.stream(fields)
                .filter(field -> !field.isAnnotationPresent(IgnoreAppRecord.class)) // 忽略被 @IgnoreAppRecord 标记的字段
                .map(field -> {
                    // 如果有 @AppRecordName 注解,则使用注解中的值作为字段名
                    if (field.isAnnotationPresent(AppRecordName.class)) {
                        return field.getAnnotation(AppRecordName.class).value();
                    }
                    // 否则,使用默认字段名
                    return field.getName();
                })
                .collect(Collectors.toList());
        
        // 将 List 转换为类似 ["id", "username", "email"] 的字符串
        return fieldNames.toString();
    }
    public static void main(String[] args) {
        // 假设有个 User 类
        class User {
            private Long id;
            
            @AppRecordName("user_name") // 自定义字段名
            private String username;
            
            @IgnoreAppRecord // 忽略该字段
            private String password;
            
            private String email; // 默认使用属性名
        }
        // 调用方法获取字段名的字符串表示
        String result = AppRecordUtil.getFieldNamesAsString(User.class);
        System.out.println(result); // 输出: [id, user_name, email]
    }
}

运行输出结果:

[id, user_name, email]

扩展:类级别忽略字段

有时我们可能想忽略多个字段,为此可以使用一个类级别的注解,来批量忽略字段。如下:

// 自定义注解:用来忽略多个字段 (类级别)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IgnoreAppFields {
    String[] value(); // 要忽略的字段集合
}

在工具类中,我们可以通过这个注解来处理类级别的字段忽略逻辑:

IgnoreAppFields ignoreAppFields = clazz.getAnnotation(IgnoreAppFields.class);
List ignoreFields = ignoreAppFields != null ? Arrays.asList(ignoreAppFields.value()) : List.of();

通过 反射 和 自定义注解,我们可以灵活地将类的属性名提取出来,并根据需求生成自定义格式的字符串。这种方式不仅通用性强,还可以通过注解实现对字段的自定义控制,适用于复杂的类结构。