You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求基于Java反射API递归遍历深度嵌套自定义对象实例的代码片段或工具库

Recursively Traverse Java Object Instances via Reflection (Check Null/Non-Null Fields)

Got it, you need to recursively walk through live object instances (not just class metadata) using Java Reflection, inspect which fields are null or non-null, and inject your custom business logic during the traversal. Let's break this down with a reusable code snippet tailored to your example classes, plus clear explanations for key parts.


Step 1: Define a Custom Logic Hook

First, create a functional interface to hold your business logic—this lets you easily swap in different behaviors without modifying the traversal code:

// Functional interface to encapsulate your custom field processing logic
@FunctionalInterface
interface FieldProcessor {
    void process(String fieldPath, Object fieldValue, boolean isNull);
}

Step 2: Core Traversal Tool Class

This class handles the recursive traversal, handles edge cases like circular references, and applies your custom logic to each field:

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

public class ObjectInstanceTraverser {

    // Track visited objects to avoid infinite loops from circular references
    private final Set<Object> visited = new HashSet<>();

    public void traverse(Object rootObject, FieldProcessor processor) {
        if (rootObject == null) {
            return;
        }
        // Skip if we've already processed this object (prevents stack overflow from loops)
        if (!visited.add(rootObject)) {
            return;
        }

        Class<?> clazz = rootObject.getClass();
        // Iterate over all declared fields (including private ones)
        for (Field field : clazz.getDeclaredFields()) {
            // Skip static fields—we only care about instance-specific data
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }

            try {
                field.setAccessible(true); // Grant access to private fields
                Object fieldValue = field.get(rootObject);
                String fieldPath = clazz.getSimpleName() + "." + field.getName();

                // Run your custom logic on this field
                processor.process(fieldPath, fieldValue, fieldValue == null);

                // Recurse into nested structures if the field isn't null
                if (fieldValue != null) {
                    Class<?> fieldType = fieldValue.getClass();
                    if (isCustomObject(fieldType)) {
                        // Drill into custom objects (like your AddOnSubscription or BeneficiaryExternalID)
                        traverse(fieldValue, processor);
                    } else if (fieldType.isArray()) {
                        // Handle array types
                        Object[] array = (Object[]) fieldValue;
                        for (Object item : array) {
                            traverse(item, processor);
                        }
                    } else if (fieldValue instanceof Collection<?>) {
                        // Handle collections (like List<String> in AddOnSubscription)
                        Collection<?> collection = (Collection<?>) fieldValue;
                        for (Object item : collection) {
                            traverse(item, processor);
                        }
                    }
                    // Primitive/wrapper types, enums, and built-ins are skipped—no nested fields to traverse
                }
            } catch (IllegalAccessException e) {
                // Adjust error handling based on your needs
                throw new RuntimeException("Failed to access field: " + field.getName(), e);
            }
        }
    }

    // Helper to identify custom objects (exclude built-in Java types)
    private boolean isCustomObject(Class<?> clazz) {
        return !clazz.isPrimitive()
                && !clazz.isEnum()
                && !Collection.class.isAssignableFrom(clazz)
                && !Map.class.isAssignableFrom(clazz)
                && !clazz.getName().startsWith("java.lang.")
                && !clazz.getName().startsWith("java.util.")
                && !clazz.isArray();
    }
}

Step 3: Example Usage with Your Object Instance

Using your sample GetAddOnSubscriptionsRequest instance, here's how to apply the traverser and run your custom logic:

First, your object instance (adjusted with valid default values as you mentioned):

// Your sample object instance
GetAddOnSubscriptionsRequest addOnSubscriptionsRequest = new GetAddOnSubscriptionsRequest()
    .setSubscriptionId("1234578")
    .setOffset(0L)
    .setPageSize(50L)
    .setAddOnSubscriptionsOutput(
        new GetAddOnSubscriptionsOutput.Builder()
            .setTotalAddOnSubscriptionsCount(10L)
            .setAddOnSubscriptions(
                new AddOnSubscription.Builder()
                    .setAsin("B0XYZ123")
                    .setAddOnIDs(List.of("ADDON_001", "ADDON_002"))
                    .setBaseSubscriptionID("BASE_SUB_789")
                    .setHasOpenProblems(false)
                    .build()
            ).build()
    );

Then, run the traversal with your custom logic (e.g., print field details or validate non-null requirements):

public class Main {
    public static void main(String[] args) {
        ObjectInstanceTraverser traverser = new ObjectInstanceTraverser();

        // Define your custom business logic here
        FieldProcessor myCustomProcessor = (fieldPath, fieldValue, isNull) -> {
            String status = isNull ? "NULL" : "NON-NULL";
            String valueStr = isNull ? "null" : fieldValue.toString();
            System.out.printf("Field: %s | Status: %s | Value: %s%n", fieldPath, status, valueStr);

            // Example custom logic: Enforce non-null on @NonNull annotated fields
            // if (field.isAnnotationPresent(NonNull.class) && isNull) {
            //     throw new IllegalArgumentException("Required field is null: " + fieldPath);
            // }
        };

        // Start traversing your object instance
        traverser.traverse(addOnSubscriptionsRequest, myCustomProcessor);
    }
}

Sample Output

You'll get output like this (truncated for brevity):

Field: GetAddOnSubscriptionsRequest.subscriptionId | Status: NON-NULL | Value: 1234578
Field: GetAddOnSubscriptionsRequest.statusFilter | Status: NULL | Value: null
Field: GetAddOnSubscriptionsRequest.pageSize | Status: NON-NULL | Value: 50
Field: GetAddOnSubscriptionsRequest.offset | Status: NON-NULL | Value: 0
Field: GetAddOnSubscriptionsRequest.addOnSubscriptionsOutput | Status: NON-NULL | Value: com.yourpackage.GetAddOnSubscriptionsOutput@1234abcd
Field: GetAddOnSubscriptionsOutput.addOnSubscriptions | Status: NON-NULL | Value: com.yourpackage.AddOnSubscription@5678wxyz
Field: AddOnSubscription.subscriptionID | Status: NULL | Value: null
Field: AddOnSubscription.addOnIDs | Status: NON-NULL | Value: [ADDON_001, ADDON_002]
...

内容的提问来源于stack exchange,提问作者Voonna Sandeep

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 06:02:34