Is there any shortcut for checking if an objects all elements(and also their elements) are null. For example I want to check If a country object(and its city and its street) is null or not? I don't want to check them one by one like;
country == null || country.city == null || country.city.street == null
public class Country{
City city;
....
}
public class City{
Street street;
....
}
public class Street{
....
}
There is no shortcut: you have to browse recursively your instances and check their fields. You could use a custom version of the browser shown in How to list all properties exposed by a Java class and its ancestors in Eclipse?. Anyway that approach would make sense if you are working with esoteric legacy code, whereas if you are writing new classes is better enforce all not-nulls in constructor of factory methods.
Related
Is there a way to avoid calling this.field for every field in a class ?
public class Test {
private String name;
private String email;
public Test(String name, String email) {
// I want to avoid this
this.name = name;
this.email = email;
}
public Test(Test test) {
// Something like this would be perfect, setting both name and email to test
this(test);
}
}
The use of this is only required in cases of name collisions, to resolve the ambiguity.
Some programmers like me prefer using the this. prefix routinely, whereas other use only where necessary.
See Answer by Wasserman for an example of how to avoid naming collision.
Use the IDE, Luke
Your IDE will generate constructors, accessors (getters/setters), equals & hashCode, toString, and so on. So you need not type this.; let the machine do the typing.
Use custom settings to control whether you want the IDE to include or omit this. prefixes.
record
You may be interested in using the records feature, new in Java 16+. A record is a brief way to write a class whose main purpose is to communicate data transparently and immutably.
With a record, by default, the compiler implicitly writes the constructor, getters, equals & hashCode, and toString. The implicitly created constructor populates each and every member field on your behalf. You write none of that code.
Here is your entire example class when written as a record. No this required. All your member fields are automatically assigned.
public record Test ( String name , String email ) {}
Be cautious in using records. The reason for their invention was not writing less code. The reason was to provide an explicit mechanism for transmitting immutable data transparently, a “nominal tuple” in academic-speak. Less boilerplate coding is merely a nice side-effect. I highly recommend reading JEP 395 for more explanation.
Tip: You can combine the two points of this Answer. Ask your IDE to generate a full-blown class by starting with a record.
Write a record with all your member fields listed in the parentheses.
Invoke your IDE to convert from a record to a class.
Voilà, you have a complete class with constructor, accessors, equals & hashCode, and toString all written out with an absolute minimum of typing by you.
For example, in IntelliJ 2022, choosing Convert record to class from the light-bulb icon menu turns this:
public record Test ( String name , String email ) {}
… into this:
package work.basil.example.recs;
import java.util.Objects;
public final class Test
{
private final String name;
private final String email;
public Test ( String name , String email )
{
this.name = name;
this.email = email;
}
public String name ( ) { return name; }
public String email ( ) { return email; }
#Override
public boolean equals ( Object obj )
{
if ( obj == this ) { return true; }
if ( obj == null || obj.getClass() != this.getClass() ) { return false; }
var that = ( Test ) obj;
return Objects.equals( this.name , that.name ) &&
Objects.equals( this.email , that.email );
}
#Override
public int hashCode ( )
{
return Objects.hash( name , email );
}
#Override
public String toString ( )
{
return "Test[" +
"name=" + name + ", " +
"email=" + email + ']';
}
}
Caveat: That result may not be the default. I may have altered the settings in IntelliJ.
Sorry, the only way to avoid this is to have different names for your constructor parameters and for your class fields.
public Test(String _name, String _email) {
// I want to avoid this
name = _name;
email = _email;
}
That said, you might have better luck using Java 16+'s record syntax.
As suggested, using records is the easiest way:
public record Test (String name, String email) {
}
That's all you need. What you then get:
A constructor that takes all arguments, in the same order as the field list
A method for each field. This does not start with get. In this case, the methods are name() and email().
equals, hashCode and toString implementations that use all fields.
There is no need for a copy constructor, because every field is automatically final.
If you want, you can add extra constructors. However, they must delegate to the automatically generated constructor, because that's the one that sets the fields. Adding additional utility methods is also fine.
And if needed, you can add validation to the generated constructor. There's special syntax that allows you to omit all the field names:
public record Test (String name, String email) {
public Test {
Objects.requireNonNull(name);
Objects.requireNonNull(email);
}
}
The assignments are done for you, there's no need to type those either.
You need this.x everytime, if there are 2 or more variables, which are called x and you want to call the attribute variable x.
The this keyword is used, to point on an attribute variable of the created instance (object) of the class.
There could be an attribute, that is called x, and a local variable which is called x too.
I have a criteria builder chain of gets, but they can be null which means they will fail.
example:
predicates.add(cb.equal(house.get("adressInfo").get("streetname"),value)
If for example house.get(adressInfo) is empty I still want it returned with an empty list or just null values for everything is fine.
I only need to filter out for as an example houses with street name "A" but must also include all houses that have an empty adressInfo.
Now I get a null.streetname invalid access error because a house has an adressInfo of null
You could maybe work around this by introducing helper methods like this:
private String getStreet(House house) {
AddressInfo addressInfo = (AdressInfo) house.get("adressInfo");
return addressInfo == null ? null : addressInfo.get("streetname");
}
And then you can do:
predicates.add(cb.equal(getStreet(house), value)
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");
I'm trying to get immutable Object that sets default value to all null fields.
So my object is:
import lombok.Builder;
import lombok.Value;
#Value
#Builder
public class Test {
#Builder.Default
String name = "default";
}
and I expect these two tests to pass:
assert Test.builder().build().name == "default"
assert Test.builder().name(null).build().name == "default"
but second one isn't passing.
How can I tell lombok not to set actually null in such case?
I have value that comes from another service and it's nullable. I want to use default value if it came null to me.
Lombok's #Builder.Default remembers whether the corresponding builder setter method has been called or not. This allows explicitly overwriting the default value even with null. There is no built-in way to "unset" the value.
There are two ways to solve this:
Remove the default value and #Builder.Default from name. Then manually implement an all-args constructor that checks whether the parameter name is null and assign the default value in that case.
Customize the builder setter method such that passing null will unset it:
public static class TestBuilder {
public TestBuilder name(String name) {
this.name$value = name;
this.name$set = name != null;
return this;
}
}
I suggest you use the latter approach, because then you won't have to modify your manual all-args constructor every time you add or remove a field in that class.
I have a use case as below.
POJO
If POJO is not null and not empty, add the pojo to a set.
Add one element from pojo to another set
Add another element from pojo to a list
I have something as below.
Optional.ofNullable(pojo)
.ifPresent(uniquePojoSet::add) //Added pojo to a set
.//Add city from pojo to a set
.//Add userName from pojo to a list
Here, ifPresent returns void. I am not sure how to handle this? I need to add only if the elements are not null and not empty.
Please advise to be written in a standard way also.
Current implementation:
if(pojo != null && !pojo.isEmpty()) {
//Add Pojo to set
if(pojo.getProperty1() != null) {
//Add to another set
}
if(pojo.getProperty2() != null) {
//Add to another list
}
}
I referred this But couldn't match my requirement exactly
The article you referred suggests converting POJO fields to Optionals. Another option is to make getters return Optionals, for example:
public class Pojo {
private String city;
private String username;
public Optional<String> getCity() {
return Optional.ofNullable(city);
}
public Optional<String> getUsername() {
return Optional.ofNullable(username);
}
}
And then you could use them as follows:
Optional.ofNullable(pojo).ifPresent(p -> {
uniquePojoSet.add(p);
p.getCity().ifPresent(cities::add);
p.getUsername().ifPresent(usernames::add);
});
Since Optional is a value-class and immutable you can store immediate steps and do:
Optional<Pojo> notEmpty = Optional.ofNullable(pojo).filter(pojo -> !pojo.isEmpty());
notEmpty.ifPresent(uniquePojoSet::add);
notEmpty.map(Pojo::getCity).ifPresent(citySet::add);
notEmpty.map(Pojo::getUserName).ifPresent(userNameSet::add);
I think the more readable way would be a simple if cascade.