We have few datatypes defined for our service response and request objects in a model. Recently we found a need of implementing ToString, HashCode and Equals on all such types to make use of these over comparison and assertions. Confirming from few source like What issues should be considered when overriding equals and hashCode in Java?, Right way to implement equals contract etc we followed implementing toString, equals and hashcode using org.apache.commons.lang3.builder.EqualsBuilder, HashCodeBuilder and ToStringBuilder as follows -
Response.java
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class Response {
private Integer value;
private Currency currency;
private Object edited;
public Response() {
}
public Response(Integer value, Currency currency, Object edited) {
this.value = value;
this.currency = currency;
this.edited = edited;
}
public Currency getCurrency() {
return currency;
}
public void setCurrency(Currency currency) {
this.currency = currency;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public Object getEdited() {
return edited;
}
public void setEdited(Object edited) {
this.edited = edited;
}
#Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Response Response = (Response) o;
return new EqualsBuilder().append(value, Response.value).append(currency, Response.currency)
.append(edited, Response.edited).isEquals();
}
#Override public int hashCode() {
return new HashCodeBuilder(17, 37).append(value).append(currency).append(edited).toHashCode();
}
#Override public String toString() {
return "Response{" + "value=" + value + ", currency=" + currency + ", edited=" + edited + '}';
}
}
Currency.java
public enum Currency {
INR
}
On implementing these using the default library version, there is a thought around enums that comes to our mind -
Is it correct to use the default hashcode and equals from the library when a datatype might contain parameters including enums as well?
Is there a library(within commons would be great) support to implementing a correct optimized solution to overriding implementation of hashcode and equals?
On a side note does the library implementation needs an improvement here or is it correct to what exists?
Edit: Have added the implementation over an Object field(edited) in the class as well. The concern there being same if I override the hashCode and equals implementation for these as well.
Do I end up using an Object's hashcode which is different for different instances as it is mostly the memory mapped address?
Edit 2: I can also see a concern raised on the inconsistent implementation on HashCode for Enum values on JIRA
Related
So, I have two Sets with elements of my class Capability.
public class Capability {
private String name;
public Capability(){
//
}
public Capability(String name){
this.name = name;
//this.id = count.getAndIncrement();
}
public String getName(){
return name;
}
#Override
public String toString(){
return "Capability: "+name+".";
}
}
Please disregard the value of this class over a String, this is for future expansion.
I'm trying to compare two sets that I've gotten from importing a json file, so they are not the same object, nor contain the same object, just have the same content.
public boolean allCapabilitiesMet(){
int count = 0;
for(Capability taskCap : this.getReqCapabilities()){
for(Capability primCap : this.getPrimitive().getCapabilities())
{
System.out.println(taskCap.equals(primCap));
System.out.println(taskCap.getName().equals(primCap.getName()));
if(taskCap.equals(primCap)){
count++;
}
}
}
return count == this.getReqCapabilities().size();
//return this.getPrimitive().getCapabilities().containsAll(this.getReqCapabilities());
}
The goal is to see if one set is a subset of the other, which I could do with the commented return before I switched to importing from the json file.
The thing is, I could fix this right now by simply changing the if clause to the string comparison, because that does indeed work. This would be terrible once I start adding other fields to the main class.
Is there anything I can do to compare the sets content without manually checking their content?
So I just replaced the equals() and hashCode() methods in Capability after adding an id field.
#Override
public boolean equals(Object obj){
if(this == obj)
return true;
if(obj == null || obj.getClass() != this.getClass())
return false;
Capability cap = (Capability) obj;
return (cap.getName().equals(this.getName()) && cap.getId() == this.getId());
}
#Override
public int hashCode()
{
return (int) this.id;
}
With this, I can use the solution that I'd originally planned for the comparison
public boolean allCapabilitiesMet(){
return this.getPrimitive().getCapabilities().containsAll(this.getReqCapabilities());
}
Is there any issue with this implementation? Sadly, I'll have to add a term to the if statement everytime I want to add a field to Capability. Is there any other way?
I am working on a project, but I got stuck in some issue. I need to test equality of child class (DiscountProduct).
Relation between classes can be understood as: Order class has-a Product and Product is parent of DiscountProduct. I want to test DiscountProduct for equality. Any help is much appreciated. Thank you!
Below is the code:
Order Class:
public class Order implements Comparable<Order>{
private int quantity;
//composition has-a relationship
private Product product;
public Order(int quantity, Product product) {
this.quantity = quantity;
this.product = product;
}
public boolean equals(Object obj){
if (obj instanceof Order) {
return product.equals(obj) && quantity == ((Order) obj).quantity;
}
return false;
}
public Product getProduct() {
return product;
}
}
Product class
public class Product implements Comparable<Product> {
private String productCode;
private String description;
private int unitPrice; //pence
public Product(String productCode, String description, int unitPrice) {
this.productCode = productCode;
this.description = description;
this.unitPrice = unitPrice;
}
//equals method to test product for equality.
public boolean equals(Object obj){
if (this == obj){
return true;
}
if (obj == null || !(obj instanceof Product)){
System.out.println("-");
return false;
}
Order other = (Order) obj;
if (!productCode.equals(other.getProduct().getProductCode()))
return false;
if (!description.equals(other.getProduct().getDescription()))
return false;
if (unitPrice != (other.getProduct().getUnitPrice()))
return false;
return true;
}
}
DiscountProduct class
public class DiscountProduct extends Product {
private double discountRate;
public DiscountProduct(String productCode, String description, int unitPrice, double discountRate) {
super(productCode, description, unitPrice);
this.discountRate = discountRate;
}
//equals method to test discount product for equality.
public boolean equals(Object obj){
Order other = (Order) obj;
//how to test for the equality for the discountProduct's field discountRate?
//does I need to add some method in Order class to get the discountRate of object of Order class,
//because relation is 'Order has Product' and then there is a parent-child relation
//between product and DiscountProduct class.
}
}
DiscountProduct could do something like:
#Override // always always always use that annotation when overriding!
public boolean equals(Object obj){
.. this == obj test
if (obj == null || !(obj instanceof DiscountProduct)){
return false;
}
if (super.equals(obj)) {
cast to DiscountProduct and check discountRate
The point here is:
you really only want objects to be equal that are of the same class. Otherwise there is a high chance that you get the necessary if a.equals(b) then b.equals(a) rule wrong
then you want to re-use the existing implementation of the parent class
to then finally, compare the child class field(s)
It might also make sense to declare that "last" version of equals() to be final, but that really depends on context.
In this part of the code:
public class Order implements Comparable<Order>{
...
public boolean equals(Object obj){
if (obj instanceof Order) {
return product.equals(obj) && ...;
}
...
}
}
A Product is comared with an Order. This comparison should obviously return false since a Product is no Order. What you probably meant to write was:
return product.equals(obj.product) && ...;
A remark on your code: There is a contract between Object::equals and Object::hashCode:
The general contract of hashCode is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
To conform with this contract, one should always override hashCode() when one overridest equals(...).
I would also encourage you to set methods equals(...) and hashCode() final. Otherwise, the contract of equals(...) could be violated.
I have a POJO something like the one mentioned below. Here I'm referring Set collection attribute in POJO1. I understand that set does not contain duplicate. Do I need to override equals() and hashCode() methods in POJO2? Using a Set here is not really going to helpful unless we override equals and hashCode methods? Please help me to understand little bit more on this context!
public class POJO1 {
private String name;
private Set<POJO2> pj2;
public Company(){
pj2 = new HashSet<>();
}
//setter and getter methods
}
Yes the only way for Java to understand which objects are duplicates is to call equals() method. Default implementation of equals() checks that references of two objects point to the same location in memory.
But depending on exact implementation of your Set you might need to override hashCode/equals or implement Comparable interface.
Since you put objects of POJO2 into HashSet you need to verride hashCodeequalsmethods inPOJO2` class.
You do like this
import java.util.Set;
public class POJO1 {
private String name;
private Set<POJO2> pojo2;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<POJO2> getPojo2() {
return pojo2;
}
public void setPojo2(Set<POJO2> pojo2) {
this.pojo2 = pojo2;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
POJO1 pojo1 = (POJO1) o;
if (name != null ? !name.equals(pojo1.name) : pojo1.name != null) return false;
return pojo2 != null ? pojo2.equals(pojo1.pojo2) : pojo1.pojo2 == null;
}
#Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (pojo2 != null ? pojo2.hashCode() : 0);
return result;
}
}
Learn more at https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#equals(java.lang.Object)
I have a custom class as my key in my hashmap like so
// In the main function
HashMap<Drink, boolean> drinkMap = new HashMap<>();
// What I would like to be able to do:
drinkMap.get("beer");
// My drink Class which is used as the key
public class Drink implements Comparable<String> {
private String name;
private String info;
public String getName() {
return Name;
}
public Drink(String name, String info) {
this.name = name;
this.info = info;
}
}
What I want to do is have the get method for the hashmap compare the string that is passed in to Drink.name and if they are the same then return that hashmap entry, but I cannot figure out how to get this to work.
I tried implementing the equals and hashcode methods in my Drink class like so:
#Override
public int hashCode() {
return Name.hashCode();
}
#Override
public boolean equals(Object o) {
return o instanceof String && o.equals(Name);
}
But when I would do hashMap.get("beer") it kept returning null even though I know there exists a Drink object with the name "beer" in the map.
This is a terrible idea. You should always query a map with the same type (or a subtype thereof) as the intended key. Not doing that only opens you up to problems (as I'm sure you've started to notice).
You should consider either making the key of your map a String type, or querying your map by Drink.
(As to why your specific case isn't working: "beer".equals(drink) != drink.equals("beer").)
I'm making a web app that queries an SQL db. I'm under the impression that I need to use entity classes and facade classes to allow persistence - across the whole site. The entity class templates have hashcodes and 1.) Im not sure if I need them and 2.) If I do, they want int's but all I have are String so, how to convert them to int and then back to String? Because I need the String value to appear on the site and the hash wants int's.
heres the code (imports have been remove to protect the innocent...):
#Embeddable
public class ComputerOwnersPK implements Serializable {
#Basic(optional=false)
#NotNull
#Column(name="Computer_Name")
private int computerNameId;
#Basic(optional=false)
#NotNull
#Column(name="User_ID")
private int userId;
public ComputerOwnersPK() {
}
public ComputerOwnersPK(int computerNameId,int userId) {
this.computerNameId=computerNameId;
this.userId=userId;
}
public int getComputerNameId() {
return computerNameId;
}
public void setComputerNameId(int computerNameId) {
this.computerNameId=computerNameId;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId=userId;
}
#Override
public int hashCode() {
int hash=0;
hash+=(int) computerNameId;
hash+=(int) userId;
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if(!(object instanceof ComputerOwnersPK)) {
return false;
}
ComputerOwnersPK other=(ComputerOwnersPK) object;
if(this.computerNameId!=other.userId) {
return false;
}
if(this.userId!=other.userId) {
return false;
}
return true;
}
#Override
public String toString() {
return "entity.ComputerOwnersPK[ computerNameId="+computerNameId+", userId="+userId+" ]";
}
}
Based on your comments I'm assuming you want computerNameId and userId to be Strings in your mapping and you have them mapped to ints because you don't know how to do the hashcode stuff.
In your hashCode method you should be able to concatenate the strings and then call hashcode on them. Very similar to what you are already doing.
private String computerNameId;
private String userId;
#Override
public int hashCode() {
// concatenate the interesting string fields
// and take the hashcode of the resulting String
return (computerNameId + userId).hashCode();
}
Make sure in your equals method you also change from != operators to !.equals method call for checking equality. Finally make sure you are keeping the contract between equals and hashCode or you could be in for some nasty surprises. Two objects that are equal must also have the same hashCode. Two objects that have the same hashCode may or may not be equal.