theMayer
theMayer

Reputation: 16167

Java - Checking Partial Object Equality

I have a use case for filtering a set of objects based on multiple properties of the object. An approach that I would use in Javascript might be to create an object with only the properties that I care about, then check those property names against the objects I wish to filter.

Example (in Typescript):

example(): void {
    let template = {
        name: 'Test',
        color: 'Blue'
    };

    let objects = [
        { name: 'Test', color: 'Blue' },
        { name: 'Test2', color: 'Blue' },
        { name: 'Test', color: 'Red' },
        { name: 'Test', color: 'Blue', extra: true },
    ];

    let results = objects.map(o => this.compare(o, template));
    console.log(results);
}

compare(obj, template): boolean {
    for (const prop in template) {
        if (obj[prop] !== template[prop]) {
            return false;
        }
    }

    return true;
}

The above will write [true, false, false, true] to the console. The reason this is cool is that it will accept basically any object and issue the correct results. It's not obvious to me how to provide this feature in Java, such that I could avoid having to implement a comparison function on hundreds of data model beans.

Is there a built-in or commonly-used way to do the equivalent comparison in Java in a generic manner?

Upvotes: 0

Views: 1317

Answers (2)

Alex Filatov
Alex Filatov

Reputation: 5052

This can be done using reflection:

public static boolean compare(Object object, Object template) {
  Field[] templateFields = template.getClass().getDeclaredFields();
  try {
    for (Field templateField : templateFields) {
      Field objectField = object.getClass().getDeclaredField(templateField.getName());
      templateField.setAccessible(true);
      objectField.setAccessible(true);
      if (!objectField.get(object).equals(templateField.get(template))) {
        return false;
      }
    }
  } catch (Exception e) {
    return false;
  }
  return true;
}

(This code just shows the idea and doesn't cover all possible cases e.g. doesn't check inherited fields)

I'm not aware of built-in or commonly-used way to do this. There are utilities for strict comparison (usually intended for unit testing), not sure they will work for your use case.

Note that Java 9 modules can restrict reflective access to objects.

Upvotes: 1

Wim Deblauwe
Wim Deblauwe

Reputation: 26858

You can something like this using streams:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

class MyObjectTest {

    @Test
    void test() {
        MyObject template = new MyObject("Test", "Blue");

        List<MyObject> objects = new ArrayList<>();
        objects.add(new MyObject("Test", "Blue"));
        objects.add(new MyObject("Test2", "Blue"));
        objects.add(new MyObject("Test", "Red"));
        objects.add(new MyObject("Test", "Blue", true));

        List<MyObject> results = objects.stream()
                                        .filter(myObject -> myObject.getName().equals(template.getName())
                                                && myObject.getColor().equals(template.getColor()))
                                        .collect(Collectors.toList());
        System.out.println("results = " + results);
    }

    static class MyObject {
        private final String name;
        private final String color;
        private final boolean extra;

        public MyObject(String name, String color) {
            this.name = name;
            this.color = color;
            this.extra = false;
        }

        public MyObject(String name, String color, boolean extra) {
            this.name = name;
            this.color = color;
            this.extra = extra;
        }

        public String getName() {
            return name;
        }

        public String getColor() {
            return color;
        }
    }
}

Upvotes: 0

Related Questions