bdetweiler
bdetweiler

Reputation: 1564

Design pattern compatible with reflection

I have some code that relies on reflection (working with a third-party libarary) to get the fields of a class. Many of these classes use a mix of the same fields, some nearly identical but with a few differences.

This has led to a ton of code duplication, but I'm not sure of a nice pattern around it due to the reliance reflection.

According to the documentation:

Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private fields, but excludes inherited fields.

This rules out an Abstract class or any inheritence-related solutions.

Here's a code example (MyField represents a third-party library object, and myData is a public member of MyField):

ClassA.java

public class ClassA {
    private final Field[] myFields;

    private final MyField a = new MyField(1);
    private final MyField b = new MyField(2);
    private final MyField c = new MyField(3);

    public ClassA() {
        myFields = this.getClass().getDeclaredFields();
    }

    public void doStuff(Field[] myFields) {
        // As an example, this clears out the field values.
        // There are several of these methods that use or manipulate the fields
        for (Field f : myFields) {
            if (f.getType() == MyField.class
                || f.getType().getSuperclass() == MyField.class) {
                try {
                    f.setAccessible(true);
                    ((MyField) f.get(this)).myData.reset();
                }
                catch (IllegalArgumentException | IllegalAccessException e) {
                    continue;
                }
            }
        }
    }

    public Field[] getFields() {
        return Arrays.copyOf(myFields, myFields.length);
    {
}

ClassB.java

public class ClassB {
    private final Field[] myFields;

    private final MyField a = new MyField(1);
    private final MyField c = new MyField(3);
    private final MyField d = new MyField(4);

    public ClassB() {
        myFields = this.getClass().getDeclaredFields();
    }

    public void doStuff(Field[] myFields) {
        // Same as above
    }

    public Field[] getFields() {
        return Arrays.copyOf(myFields, myFields.length);
    {
}

Note that ClassA and ClassB share fields a and c and methods getFields() and doStuff().

Is there any way to shore up some of this duplication without affecting the reflection?

Upvotes: 0

Views: 478

Answers (1)

kaya3
kaya3

Reputation: 51034

Why do the fields need to be stored in an array within the class? It seems that obj.getFields() is always equivalent to obj.getClass().getDeclaredFields(). Note that getDeclaredFields returns a new array each time anyway, so calling it once and then making a defensive copy on your own getter is superfluous.

If you prefer the readability of obj.getFields() then you could make this a default interface method, and inherit it on each class from the interface without having to change the class hierarchy:

public interface FieldGetter {
    default Field[] getFields() {
        return this.getClass().getDeclaredFields();
    }
}

Example:

> class A implements FieldGetter { int x; String y; }
> new A().getFields()
Field[2] { int A.x, java.lang.String A.y }

It's not clear whether your doStuff method does the same thing on each class; if so, you can make that a default interface method, too.

By the way, the fact that getDeclaredFields doesn't return superclass fields isn't a barrier to having a class hierarchy, because you can call it on the superclasses too; see this other question.

Upvotes: 1

Related Questions