Generic interface with equals and hashCode methods, is it possible? - java

I'd like to define an interface that will be used to implement keys for a map. The map uses equals and hasCode to locate and compare keys. My keys need to override these with custom computations. Here is what I'd like to do. It's important that the complex key is used. Thanks
public interface CachedRequestKey<T>{
public T complexKeyObject;
#Override
public boolean equals(T obj);
}
EDIT for down voters: I'm aware the above code is not valid. I'm looking for ideas to achieve an interface that insures the implementing class provides required methods using the generic type.

What you want isn't possible in Java. Object already implements equals and hashCode, so every subclass does automatically as well. There's no way for an interface or abstract class to require it to be reimplemented. It's assumed that equals and hashCode for a given class are implemented correctly for that class.
If you really need your classes to implement specific comparison operations and don't want to use the existing equality methods by accident, you could define your own comparison functions on you interface and require implementing classes to define those. Classes that already implement your equality mechanics in their equals and hashCode methods can delegate to the existing methods.

Related

Overriding equals method to compare functional interfaces

I am implementing equals method for a class, which has a member of a Functional Interface type. I do not have a separate class for the concrete implementation of the interface, and I use lambda expression to create and set the interface variable. I want the equals method of the class to take into consideration, the code, which was provided via lambda expression. Different implementations must must not return equal. Is there a way to achieve this properly? Since I did not find any links related to this requirement I am doubting whether such a practice is recommended, even if it is possible to achieve the same. What is the best thing to do?

Why does the Collection interface have equals() and hashCode()?

Why does the Collection interface have equals(Object o) and hashCode(), given that any implementation will have those by default (inherited from Object) ?
From the Collection JavaDoc:
While
the Collection interface adds no stipulations to the general contract
for the Object.equals, programmers who implement the Collection
interface "directly" (in other words, create a class that is a
Collection but is not a Set or a List) must exercise care if they
choose to override the Object.equals. It is not necessary to do so,
and the simplest course of action is to rely on Object's
implementation, but the implementor may wish to implement a "value
comparison" in place of the default "reference comparison." (The List
and Set interfaces mandate such value comparisons.)
The general contract for the Object.equals method states that equals
must be symmetric (in other words, a.equals(b) if and only if
b.equals(a)). The contracts for List.equals and Set.equals state that
lists are only equal to other lists, and sets to other sets. Thus, a
custom equals method for a collection class that implements neither
the List nor Set interface must return false when this collection is
compared to any list or set. (By the same logic, it is not possible to
write a class that correctly implements both the Set and List
interfaces.)
and
While the Collection interface adds no stipulations to the general contract for the Object.hashCode method, programmers should take note that any class that overrides the Object.equals method must also override the Object.hashCode method in order to satisfy the general contract for the Object.hashCode method. In particular, c1.equals(c2) implies that c1.hashCode()==c2.hashCode().
To answer your specific question: why does it have these methods? It's done simply for convenience to be able to include Java Docs giving hints as to what implementers should do with these methods (e.g. comparing equality of values rather than references).
To add to the other great answers. In the Collections interface, the equals method is defined in that interface to make some decisions in the way equaling two instances of collection should work. From the JAVA 8 documentation:
More generally, implementations of the various Collections Framework
interfaces are free to take advantage of the specified behavior of
underlying Object methods wherever the implementor deems it
appropriate.
So you don’t add methods from the Object class for any other reason that giving more definitiveness to the java doc. This is the reason why you don’t count those methods in the abstract methods in the abstract methods of an interface.
Moreover, in JAVA 8, along the same line of reasoning, default methods from the Object class are not allowed and will generate a compile error. I believe it’s was done to prevent this type of confusion. So if you try to create a default method called hashCode(), for example, it will not compile.
Here is a more in-depth explanation for this behavior in JAVA 8 from the Lambda FAQ:
An interface cannot provide a default implementation for any of the
methods of the Object class. This is a consequence of the “class wins”
rule for method resolution: a method found on the superclass chain
always takes precedence over any default methods that appear in any
superinterface. In particular, this means one cannot provide a default
implementation for equals, hashCode, or toString from within an
interface.
This seems odd at first, given that some interfaces actually define
their equals behavior in documentation. The List interface is an
example. So, why not allow this?
One reason is that it would become more difficult to reason about when
a default method is invoked. The current rules are simple: if a class
implements a method, that always wins over a default implementation.
Since all instances of interfaces are subclasses of Object, all
instances of interfaces have non-default implementations of equals,
hashCode, and toString already. Therefore, a default version of these
on an interface is always useless, and it may as well not compile.
Another reason is that providing default implementations of these
methods in an interface is most likely misguided. These methods
perform computations over the object’s state, but the interface, in
general, has no access to state; only the implementing class has
access to this state. Therefore, the class itself should provide the
implementations, and default methods are unlikely to be useful.
Just to add to the great answers above, it makes sense to have the 'equals' or `hashCode' methods in this scenario:
Collection<Whatever> list1 = getArrayList();
Collection<Whatever> list2 = getAnotherArrayList();
if(list1.equals(list2)){
// do something
}
In the absence of the equals method in the interface, we'll be forced to use concrete types, which is generally not a good practice :
ArrayList<Whatever> list1 = getArrayList();
ArrayList<Whatever> list2 = getAnotherArrayList();
if(list1.equals(list2)){
// do something
}

What isn't comparable by the Comparable class?

I know that this class compares two objects in a class(e.g. two Strings), but what types aren't comparable using this class?
That's not quite how it works. Comparable is an interface that any class may choose to implement in order to indicate that instances of the class may be compared using the class' compareTo() method. So you can choose to implement this interface in any class you create yourself (however, you must write the code for compareTo() yourself, since Java doesn't know how to compare your objects in a meaningful manner).
Some built-in classes implement Comparable and others don't - there might be a list somewhere, but it would be way too long for an SO answer. If you are wondering about whether a specific class implements compareTo(), check its documentation (and see if the comparison does what you expect) or simply try to call that method. If there is a built-in or third-party class that does not implement Comparable, you need to create a Comparator instead in order to compare them.
A Comparator may compare anything you wish, because you are the one who decides how it should work.
Actually, you can compare everything in Java.
For instance, you have a class X, which is incomparable. You can make this class an attribute to another class C and make C comparable, override its compareTo() method.
For example, it doesn't make sense to compare Colors
This is right. Even though it doesn't make sense, you can. Here is the answer for the question:
Which color is brighter?
public class ComparableColor implements Comparable<ComparableColor>
{
Color color;
public ComparableColor(Color color)
{
this.color = color;
}
#Override
public int compareTo(ComparableColor c)
{
return c.color.getAlpha() - this.color.getAlpha();
}
}
For example, it doesn't make sense to compare Colors, so this class doesn't implement the Comparable interface. Now, if you want a list of all classes that don't implement Comparable, I don't think there is one out there.

Why is it not necessary to override both methods of interface Comparator in Java

We know that it is necessary to implement all methods of an interface, if we want to make an object of that class. But why is it not necessary to implement both the methods compare() and equals() of interface Comparator in java?
I agree that the purpose is solved but even then why is it not mandatory to override equals() if we override compare()?
Since all classes implicitly extend Object every implementation of a Comparator has an equals method, because every Object has one.
It would be the same if you define an interface with a toString() method.
public interface ToString {
public String toString();
}
public class SomeClass implements ToString {
// toString implicitly implemented, because Object defines it
}
When you look at the class it says "implements ToString" and this is true, isn't it?
Because it is already overridden by java.lang.Object on every object you can create.
Every object implicitly has an equals from Object (as every object is a sub-type of Object) - and since it's a virtual method, standard Java polymorphism takes over.
Now, Comparator#equals imposes an additional restriction, which is why it is specified as part of the interface.
..this method can return true only if the specified object is also a comparator and it imposes the same ordering as this comparator.
However, since the coverse need to be true, then not overloading equals doesn't break the new requirement.
Note that it is always safe not to override Object.equals(Object).. [as then different comparator instances will never be equal].

How to implement equals method for Java anonymous class properly?

In this post I suggested a solution that uses interface and anonymous class. However, there is one thing to be implemented: the hashCode and equals method.
However I found it is hard to implement equals for anonymous class that implements an interface. In that example the interface is Pair<L,R>, and a factory method Pairs.makePair will return an anonymous implementation for it. Suppose I added an equals implementation. The user may implement their own Pair<L,R> classes with a different equals code, therefore the call userobj.equals(makepairobj) will enter their code, and makepairobj.equals(userobj) will enter my code. Because I have no control of their code, it is hard to make sure equals to be symmetric, which is required for a good implementation.
I believe this problem is common for other cases, so I would like to know how this issue being address generally?
EDIT:
In typical class, the implementation of equals will check the parameter type to make sure it is the same as its own. This guarantee only the implementing code will be called to compare the objects. However, the anonymous class do not have a name and cannot check the type with instanceof. What I can do is make sure it is an instance of the implementing interface/class. Which is not enough to prevent the above scenario.
You can use this.getClass() (with either == or isAssignableFrom()) to compare the types.
Edit
As in:
public boolean equals(Object obj) {
if (getClass() == obj.getClass()) {
// do whatever
}
return false;
}
Usually, when you make an interface like this, it requires the implementing classes to implement equals and hashCode to follow some convention. For example, if you look at the java.util.List interface, it requires lists to be equal iff they have the same length and equal elements in the same order, and it specifies a formula for calculating the hashCode based on the hash codes of the elements.
So then "it is hard to make sure equals to be symmetric" should not be a problem.
The problem you've encountered is a sign that an anonymous class is the wrong way to implement this.
Anonymous classes are a simply a shorthand way of implementing an interface or extending a class. It's purely syntactic sugar, with no extra functionality or other advantage. They were (perhaps mistakenly) intended to make your code simpler and more readable. If an anonymous class complicates your code instead, don't use it.
Many cases that used to be a good fit for anonymous classes are now better served by lambdas. If a class has two or three methods, trying to put it in an anonymous class makes your code hard to read; it should be an inner class anyway.

Categories