Compare two objects excluding some fields - Java - java

I need to compare two objects of the same class excluding some fields.
public final class Class1 {
private String a;
private String b;
private String c;
:
:
:
private String z;
private Date createdAt;
private Date updatedAt;
}
How can i find if the two objects of the above class are equal excluding createdAt and updatedAt values? Since there are a lot of fields in this class, i don't want to compare each of them one by one.
Please don't give AssertJ's recursive comparison solution as I don't need it for UnitTests.
Thank you in Advance!

If overriding Object::equals and Object::hashCode is not an option, we can use the Comparator API to construct a corresponding comparator:
final Comparator<Class1> comp = Comparator.comparing(Class1::getA)
.thenComparing(Class1::getB)
.thenComparing(Class1::getC)
.
.
.
.thenComparing(Class1::getZ);
Unfortunately, there is no way to do this without comparing all fields that should be equal.

The quickest way without writing any code is Lombok
Lombok is one of the most used libraries in java and it takes a lot of Boilerplate code off your projects. If you need to read more on what it can and does, go here.
The way to implement what you need is pretty straightforward:
// Generate the equals and HashCode functions and Include only the fields that I annotate with Include
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Getter // Generate getters for each field
#Setter // Generate setters for each field
public class Class1
{
#EqualsAndHashCode.Include // Include this field
private Long identity;
private String testStr1; // This field is not annotated with Include so it will not be included in the functions.
// ... any other fields
}
Lombok can do a lot more than this. For more information on #EqualsAndHashCode refer to this.
You can always use #EqualsAndHashCode.Exclude for a quicker solution to your use case:
#EqualsAndHashCode
#Getter // Generate getters for each field
#Setter // Generate setters for each field
public final class Class1 {
private String a;
private String b;
private String c;
:
:
:
private String z;
#EqualsAndHashCode.Exclude
private Date createdAt;
#EqualsAndHashCode.Exclude
private Date updatedAt;
}

Try overriding equals method like below :
import java.util.Date;
import java.util.Objects;
public final class Class1 {
private String a;
private String b;
private String c;
private String z;
private Date createdAt;
private Date updatedAt;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Class1 class1 = (Class1) o;
return Objects.equals(a, class1.a) && Objects.equals(b, class1.b) && Objects.equals(c, class1.c) && Objects.equals(z, class1.z);
}
#Override
public int hashCode() {
return Objects.hash(a, b, c, z);
}
}

I addition to the Comparator and hashCode()/equals method, you could also use Reflections.
Create an annotation to exclude certain fields:
Blacklisting Example:
#Retention(RetentionPolicy.RUNTIME) //
#Target(ElementType.FIELD) //on class level
public #interface IngoreForEqualCheck { /* tagging only */ }
Use Reflection to analyze the objects you want to compare, by using pClass.getFields() and/or pClass.getDeclaredFields() on the objects' class. This may be even different classes.
Iterate over all fields that are NOT tagged to be ignored, compare values.
Optimizations
To extend on the blacklisting from above, also introduce whitelisting: also create an annotation UseForEqualCheck to only check those fields.
For improved speed, when analyzing the respective class and its fields, you can create iterable lists of the fields to check, and instead of doing the Reflection fields analysis each time, simply use the lists.
Normally you would use equals() on the detected field values. You could also a) tag the class with another custom annotation, or b) check the fields for any whitelisting/blacklisting annotations, so that you will reliably use your new method for embedded/inherited/delegated annotated classes.
Warning
As with all reflections, you might get into trouble when analyzing hierarchies of classes, that have been modified during the compile process (javac) by annotation preprocessors, or by bytecode weaving. This mostly refers to Java EE aka Jakarta, but can happen anywhere where behind-the-scenes functionality is incorporated in your code, or runtime behavior is changed, like with injections, aspect oriented libraries etc.

#Renis1235 's Lombok solution is the easiest.
However if for some reason in different contexts the equals can mean different things and you don't want to change the default Equals and Hashcode behaviour, I would advice to assign default values to the fields you want to exclude, and then use equals.
(Assuming you can change the objects)
For example:
Class1 a = ...;
Class1 b = ...;
a.createdAt = null;
b.createdAt = null;
a.updatedAt = null;
b.updatedAt = null;
boolean isEqualExcludingTimestamps = a.equals(b);

Use Apache Commons-Lang
CompareToBuilder.reflectionCompare(expected, argument, "someField");

Related

How to create a single add method for different entities?

How can I create a single add method that will insert different entities in the jdbc? I want to use annotations and reflection for that.
I have created 2 annotations:
(...)
public #interface Column {
String name();
boolean isPrimaryKey() default false;
}
and
(...)
public #interface Table {
String name();
}
Suppose we have 2 entities/models/etc. : Client and Waiter. For both we should make 2 add methods, each with own INSERT.
If we have like 4 entities, we should have 4 add methods for each entity. Instead of having 4 add methods, how can I make just 1 add method? and by using the annotations and the reflection.
Eg:
#Table(name = "Table_Client")
public class Client{
#Column(name = "ID", isPrimaryKey = true)
private long id;
#Column(name = "FULL_NAME")
private String name;
}
#Table(name = "Table_Waiter")
public class Waiter {
#Column(name = "FULL_NAME", isPrimaryKey = true)
private String name;
#Column(name = "AGE")
private int age;
}
case: db.add(Client c1) => add to the database in the table Table_Client the client c1
db.add(Waiter w1) => add to the database in the table Table_Waiter the waiter w1
and so on...
My idea is to take the class of the given object and scan it for the TYPE annotation to get the table's name. Then, take all the field's annotations and make a dynamic INSERT INTO VALUES query, but the problem is that I can't actually do that, because I can't pass the object's arguments.
Another question: if this can be done, update and delete methods can follow the same path?
I cannot refrain from mentioning how many holes you may find in the road ahead. But judging from the comments, that's the path you want to explore.
First of all, regarding your existing code, you need to apply a retention meta-annotation to your annotations, Column and Table. For example:
#Retention(RetentionPolicy.RUNTIME)
public #interface Column {
String name();
boolean isPrimaryKey() default false;
}
This meta-annotation will ensure that you can read your annotations at runtime through reflection.
Then, you need to inspect the class searching for these annotations at both class and field levels.
The Class class will have everything you need. You should know you can get it from any object by calling the getClass method. It contains a couple of important methods for what you are trying to achieve:
getAnnotation(Class c) will return the annotation if it exists, or null otherwise.
getDeclaredFields will return all declared class fields, even private ones.
At a field level, the Field class provides the following methods:
getAnnotation(Class c), same as above, will return the annotation if it exists, or null otherwise.
getType will return the class associated with the field
Now consider the following piece of code:
public static void inspectClass(Class<?> cls) {
Table t = cls.getAnnotation(Table.class);
if (t != null) {
System.out.print(t.name() + " --> ");
for (Field f: cls.getDeclaredFields()) {
Column c = f.getAnnotation(Column.class);
if (c != null) {
System.out.print(c.name()
+ " "
+ f.getType().getSimpleName()
+ (c.isPrimaryKey() ? " PK" : "") + ", ");
}
}
}
}
Applying this to your Client class, for instance, would return something like:
Table_Client --> ID long PK, FULL_NAME String,
Of course, this needs some work, but the idea is there.
EDIT:
To access values of an instance through reflection at runtime, for creating a dynamic INSERT statement, that could be done by calling get method on the Field class. When dealing with private fields though, it's necessary to tweak the privacy mode first:
f.setAccessible(true);
Object value = f.get(myInstance);

Mockito when checking for specific object property value

I have the following in a working test:
when(client.callApi(anyString(), isA(Office.class))).thenReturn(responseOne);
Note that client is a Mock of class Client.
I want to change "isA(Office.class)" to tell it to match where the "id" property of an Office instance is "123L". How can I specify that I want a specific argument value in the method of a mocked object?
Edit: Not a duplicate because I'm trying to use it on a "when" and the linked question (and other resources I've found) are using ArgumentCaptor and ArgumentMatcher on "verify" and "assert". I'm thinking I can't actually do what I'm trying and will try out another way. Of course, I'm willing to be shown otherwise.
Reopening as requested, but the solution (use an ArgumentMatcher) is identical to the one in the linked answer. Naturally, you can't use an ArgumentCaptor when stubbing, but everything else is the same.
class OfficeWithId implements ArgumentMatcher<Office> {
long id;
OfficeWithId(long id) {
this.id = id;
}
#Override public boolean matches(Office office) {
return office.id == id;
}
#Override public String toString() {
return "[Office with id " + id + "]";
}
}
when(client.callApi(anyString(), argThat(new IsOfficeWithId(123L)))
.thenReturn(responseOne);
Because ArgumentMatcher has a single method, you can even make it a lambda in Java 8:
when(client.callApi(anyString(), argThat(office -> office.id == 123L))
.thenReturn(responseOne);
If you're already using Hamcrest, you can adapt a Hamcrest matcher using MockitoHamcrest.argThat, or use the built-in hasProperty:
when(client.callApi(
anyString(),
MockitoHamcrest.argThat(
hasProperty("id", equalTo(123L)))))
.thenReturn(responseOne);
I ended up going with "eq". This was ok in this case because the objects are pretty simple. First I created an object that is the same as what I expect to get back.
Office officeExpected = new Office();
officeExpected.setId(22L);
Then my 'when' statement becomes:
when(client.callApi(anyString(), eq(officeExpected))).thenReturn(responseOne);
This allows me to have better checking than "isA(Office.class)".
adding an answer for anyone with a more complex object.
answer from OP uses eq which works for simple objects.
However, I had a more complex object with many more fields. Its quite painful to create Mock object and fill in all the fields
public class CreateTenantRequest {
#NotBlank private String id;
#NotBlank private String a;
#NotBlank private String b;
...
...
}
I was able to use refEq to achieve the same thing without setting a value of each field.
Office officeExpected = new Office();
officeExpected.setId(22L);
verify(demoMock, Mockito.atLeastOnce()).foobarMethod(refEq(officeExpected, "a", "b"));

How to tell that a field is not going to be saved

I have a class which is used to get transfer data from the one application to another and then also to update if changes were made.
public class Data {
private String name;
private String number;
private String info;
... getters/setters...
}
Let's say name and number will be updated if you change them but e.g. info is not. What's the best way to tell programmers in the future that this is intended so they can recognize it immediately?
Update:
It's encoded as a JSON file and when I get it back I don't care about the info field anymore. It could be empty
You can create your custom annotation, specific to your application. If you are using any framework like Hibernate you can use #transient.
Probably not the correct way, but if you are just talking about "informing" other programmers, you could simply put the transient keyword on your info field.
But of course, that would be really "informal"; as it would probably not at all affect how your framework is dealing with your fields.
I would use serialisation combined with the transient keyword
What is object serialization?
import java.io.*;
import java.util.*;
// This class implements "Serializable" to let the system know
// it's ok to do it. You as programmer are aware of that.
public class SerializationSample implements Serializable {
// These attributes conform the "value" of the object.
// These two will be serialized;
private String aString = "The value of that string";
private int someInteger = 0;
// But this won't since it is marked as transient.
private transient List<File> unInterestingLongLongList;
There's no indication in your file that name or number are being persisted.
If you are going to put behavior into the file in some durable way, this isn't just a file, it's a representation of an object, where data and the related behavior live as one. Write a method clarifying the intent.
public boolean isStorable() {
boolean isOk = true;
isOk &= (name != null && name.length() > 0);
isOk &= (number > 0);
return isOk;
}
Makes it clear that not every one of these items contribute to being able to store the object, and that not every value within these items contribute to a valid storage state.
It also makes it clear that this object permits invalid states within its private data. That's a code smell that could indicate a design flaw. Perhaps you should look into whether that is a design flaw, and if it is, then fix it.
Start here https://docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html
Almost any programmer seing a POJO like this will know that behaviour is what you explained....
#Table(name = "data")
public class Data {
#Id
#Column(name = "name")
private String name;
#Column(name = "number")
private String number;
private String info;
... getters/setters...
}
UPDATE: It's encoded as a JSON file and when I get it back I don't care about the info field anymore. It could be empty

Android Realm copyToRealmOrUpdate creates duplicates of nested objects

I have following classes:
public class Note extends RealmObject {
#PrimaryKey
private String id;
private Template template;
// other primitive fields, getters & setters
}
public class Template extends RealmObject {
private String name;
private String color;
// other primitive fields, getters & setters
}
I get my data from backend via Retrofit & Gson, so I have ready-to-use java objects in response.
Let's imagine that backend returns me same three Notes each time I call it.
When I get the list of Note objects, I do the following:
private void fetchNotesAndSave() {
List<Notes> notes = getNotesViaRetrofit();
Realm realm = Realm.getInstance(mContext);
realm.beginTransaction();
realm.copyToRealmOrUpdate(notes);
realm.commitTransaction();
realm.close();
}
After that I call these lines to check count of stored objects:
int notesCount = mRealm.where(Note.class).findAll().size();
int templatesCount = mRealm.where(Template.class).findAll().size();
For the first time:
notesCount == 3;
templatesCount == 3;
That's right. But, if I call the server again, get same notes (same primaryKey ids), and call fetchNotesAndSave() again, I'll get these results:
notesCount == 3;
templatesCount == 6;
Each time I call copyToRealmOrUpdate(), nested objects, that are inside of objects with primaryKey are duplicated - not updated.
Is there any way to change this behaviour?
Please let me know if you need more information. Thanks in advance!
It is because your Template class doesn't have any primary key. In that case these objects are inserted again as there is no guarantee that the referenced template objects safely can be updated, even if they are part of another object that has a primary key.
If you add a #PrimaryKey to your template class it should work as you expect it to.
If you can't provide a PK as suggested, you might want to use the following work around to avoid duplicates.
for (Note note: notes) {
realm.where(Note.class)
.equalTo("id", note.getId())
.findFirst()
.getTemplate()
.deleteFromRealm();
}
realm.copyToRealmOrUpdate(notes);

Group a list of objects by multiple attributes

I am looking for a nice way to group a list of objects by multiple class attributes. The Google guava library provides a feature to group by a single attribute. I haven't found a utility yet that offers what I am looking for.
See below. I have encapsulated the grouping attributes into its own class Grouping. However, this need not be necessary if a better solution exists.
A class that contains grouping attributes.
Class Grouping{
private String key1;
private String key2;
private String key3;
//getters, setters
}
Class representing a type that would need to be grouped.
Class Groupable{
private Grouping grouping;
private String x;
private String y;
private String z;
// getters, setters
}
What I need is a Map that contains as key, a Groupable object with unique set of keys. And a list of Groupable objects as corresponding grouped items.
Map<Grouping, List<Groupable>> groupings;
Any and all suggestions welcome!
Answering my own question based on #JBNizet and #JohnBollinger's comments.
I use Eclipse IDE, so all I needed to do is to go to Source -> 'Generate hashCode() and equals()..' and invoke the same on my Grouping class to include all attributes in the overridden implementation. There on, guava library can be used in its regular way as below:
Function<Groupable, Grouping> groupFunction = new Function<Groupable, Grouping>(){
#Override
public Grouping apply(Groupable groupable) {
return groupable.getGrouping();
}
};
ImmutableListMultimap<Grouping, Groupable> groupings = Multimaps.index(lsGroupable, groupFunction);
groupings gives me what I need. lsGroupable is a list of Groupable objects.

Categories

Resources