I am struggling to understand this Koan:
#Koan
public void equalsMethodCanBeChangedBySubclassesToTestsIfTwoObjectsAreEqual() {
Object object = new Integer(1);
assertEquals(object.equals(object), true);
assertEquals(object.equals(new Integer(1)), __);
// Note: This means that for the class 'Object' there is no difference between 'equal' and 'same'
// but for the class 'Integer' there is difference - see below
}
As far as I understand, because object is an instance of the Object class, the .equals() method has not been overwritten, and therefore checks for object equality.
If new Integer(1) creates a new instance, then it should be a separate object to object. Following my train of thought, the correct answer should be false, but only true makes this pass. Where is the flaw in my logic?
Edit: I understand that integers between -128 and 127 are cached. If my understanding of the object object is correct (as stated above), then this is irrelevant.
Integer overrides equals and checks if the underlying int is equal to the int of the other Integer instance, and if so, returns true. The reason why Integer's equals method is invoked, and not the one from Object, is that the runtime type of object is Integer.
Integer is an Object, but due to the overridden equals, no object identity is used.
All following boolean expressions evaluate to true:
print((new Integer(1).equals(1)));
print((new Integer(1).equals(new Integer(1))));
print((((Integer) 1).equals(new Integer(1))));
print(((Integer) 1).equals(1));
Now consider autoboxing, which reuses instances for values in the range [-128,127]. The following statements about object equality are all evaluating to true:
1 == ((Integer) 1)
((Integer) (-128)) == ((Integer) (-128)) // in autoboxing range
((Integer) (+127)) == ((Integer) (+127)) // same
((Integer) (-200)) != ((Integer) (-200)) // not autoboxing
((Integer) (+200)) != ((Integer) (+200)) // same
((Integer) (-128)) != (new Integer(-128)) // explicit new instance, so no autoboxing
((Integer) (+127)) != (new Integer(+127)) // same
As far as I understand, because object is an instance of the Object class, the .equals() method has not been overwritten, and therefore checks for object equality.
You've got this one completely wrong. Even though the static type of variable object is Object, it remains an instance of Integer. That is why equals() is directed to Integer's override, producing the right result.
Assigning an instance to a variable of base type, or an interface implemented by the class, does not "strip" the object of its subclass behavior. In particular, an instance of Integer retains all its behaviors as implemented in the Integer class, even though you have assigned the instance to a variable of type Object.
You are calling equals on an Integer object instance. It is dispatched at run-time to the implementation in the Integer class (which regards the Integer equal to any other Integer of the same numeric value). The compile-time type (the static type of the variable involved) does not (directly) matter.
If that was not the case, how would something like this work (where the interfaces involved have no implementations at all):
Comparable<Integer> a = 1;
Serializable b = 1;
assertTrue(a.equals(b));
Note that static methods are "dispatched" at compile-time. That's why you should call them using the class name, not an object instance (which is ignored, can even be null, and the compiler issues a warning).
Heard of Dynamic Method Dispatch?
When you use a super class reference to refer to subclass object, and if subclass has overridden method, this overridden method will be called.
Hence, though you are using Object object = new Integer(1);, calling equals on object will always call Integer.equals().
And Integer.equals() checks for integers' equality, not necessarily same same reference.
If new Integer(1) creates a new instance, then it should be a separate object to object.
That's where you are going wrong. A new instance of Integer(1) will be false for == but true for equals.
Related
I'm trying to figure out what the syntax is for calling an object inside a method..
Pseudocode:
boolean check(Object someObject) {
return someObject == theOtherObject;
}
public static void main(String args[]) {
someClass one = new someClass();
someClass two = new someClass();
one.check(two);
}
So the check method is supposed to check whether the two objects are equal, but how would I specify the other object (theOtherObject should be one)?
Thanks in advance!
One word answer: this
boolean check(Object someObject) {
return someObject == this;
}
which will test object identity only. You should override equals and use that.
if (one.equals(two)) {
// ...
}
You can have the boolean check(Object o) method inside SomeClass and check
boolean check(Object o) {
this == (SomeClass) o;
}
This would work only if both reference variables are pointing to same object. Moreover the right way to check if two objects are meaningfully equal would be to use the inherited equals and hashCode method.
Override equals and hashCode method.
Why do I need to override the equals and hashCode methods in Java?
https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-
So what you're asking for there is actually already a command in java.lang.Objects class to compare to objects.
one.equals(two)
the comparison this does is called a shallow comparison. So if that's what you're looking to do then this would work. For reference, the definitions of shallow comparison defined by geeksforgeeks.org is
Shallow comparison: The default implementation of equals method is defined in Java.lang.Object class which simply checks if two Object references (say x and y) refer to the same Object. i.e. It checks if x == y. Since Object class has no data members that define its state, it is also known as a shallow comparison.
if you're looking to do a more complicated comparison you're the best bet would be to actually override the equals command in the one class file
this article would be a good place to start to learn more about this topic.
https://www.geeksforgeeks.org/equals-hashcode-methods-java/
My inquiry is on wrapping, using the Object class as my wrapper.
My goal is to take the primitive int (or a wrapper of int) and unbox it from a generic Object.
The only problem is that I will be working with mixed types at runtime, so I am unable to generically type the return type of the method. T var1 != (T) var2
What I can do right now has excessive overhead because I cannot assume the Object wraps a value of type int, so I parse it.
Object int_wrapped_in_object = 1;
Integer.valueOf(String.valueOf(int_wrapped_in_object));
//I could cast from Object to int with:
//(int) int_wrapped_in_object
//but this will always anticipate a classcastexception in case the value was wrapped with another type, like String
This works, but I would ideally like to skip a step of parsing and just unwrap box for the integer value.
Integer wrapper class does not support valueOf(Object o), likely because Object does not implement the relative cast. It (Object) supports toString(). But not toInt(). It is very peculiar because Object indeed can wrap a primitive int. Why that is the case is above my level of knowledge, so I can only work with what I have. For all I know there may even be native support for unwrapping an Object as an int.
Please help me find a solution that involves using minimal parsing and introducing as few exceptions to get the primitive int value.
Misconception on your end:
Object int_wrapped_in_object = 1;
The type of int_wrapped_in_object is Integer, not Object! The compiler already boxes for you!
In other words: there is no way that the java compiler would "box" an int into something that is "only" Object.
Thus a simple int_wrapped_in_object instanceof Integer is all you need to figure if that Object is actually an Integer. And then you can simply cast!
Your value gets wrapped to an Integer and then the view to it is reduced by casting it to Object.
You can later safely cast it back if you check the type with instanceof. Then you can unwrap it to int with the Integer#intValue method.
// Indirect conversion int -> Integer and then reduced view to Object
Object int_wrapped_in_object = 5;
if (int_wrapped_in_object instanceof Integer) {
// Checked cast as the object indeed is an Integer but with reduced view
Integer intAsInteger = (Integer) int_wrapped_in_object;
// Retrieve the int value from the Integer
// (you could also do an implicit unwrapping here) like
// int value = intAsInteger;
int value = intAsInteger.intValue();
System.out.println("The value is: " + value);
} else {
throw new IllegalStateException("Value is no Integer but it should.");
}
Note that the line
Object int_wrapped_in_object = 5;
gets indirectly transformed to
Object int_wrapped_in_object = (Object) Integer.valueOf(5);
If you need to do such conversions often then just create an utility method which does that for you.
Note that the object itself stays Integer and gets not converted to an Object, that is not how casting works, only its view is reduced. You can check that with
System.out.println(int_wrapped_in_object.getClass())
It will print class java.lang.Integer and not class java.lang.Object. So the type of an object always stays the same, even after casting. You only reduce the view to it which has advantages like higher modularity.
Having written the code below, I was wondering why clone() doesn't return the same hashcode for each additional instance. Am I doing something wrong?
public class Accessor implements Cloneable {
public static void main(String[] args) {
Accessor one = new Accessor();
Accessor two = one.clone();
System.out.println("one hahcod " + one.hashCode()
+"\ntwo hashcode " + two.hashCode());
}
public Accessor clone(){
try{
return (Accessor)super.clone();
}
catch (CloneNotSupportedException err){
throw new Error("Error!!!");
}
}
}
Since Accessor does not override hashCode, you will get the default implementation of Object.hashCode. This has implementation-defined semantics but will basically cast the address of the object to an integer, such that distinct object instances will have different hashCodes.
See What is the default implementation of `hashCode`? for more information on the above.
Note that if you are going to implement hashCode, you should also implement equals. For a good reference on equals and hashCode, read Joshua Bloch's Effective Java (or see Best implementation for hashCode method)
Because it is a different object. You are invoking the cloning inherited from Object in this case. For each new object you will have a different dashcode. If you open the source code of Object in java what you will find there is the following:
public native int hashCode();
public boolean More ...equals(Object obj) {
return (this == obj);
}
The key point here is that once you clone an object A clone of B A==B will always return false.
Then if you read the hashcode documentation it states the following :
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 hashtables.
The clone method creates a shallow copy of your first object but your Accessor class has no instance field and does not override hashCode method, as a consequence the instances of this class get the default behaviour from Object class for hashCode. This behaviour is similar as calling System#identityHashCode with your object as parameter.
public boolean equals(Object o) {
if (this == o)
return true;
if ((o == null) || (this.getClass() != o.getClass()))
return false;
else {
AlunoTE umAluno = (AlunoTE) o;
return(this.nomeEmpresa.equals(umAluno.getNomeEmpresa()) && super.equals(umAluno);
}
}
Could anyone explain me how the fourth line ((this.getClass() != o.getClass())) works when the argument is a super class? Because the classes have different names. this.getClass will return a different name than o.getClass, right?
Check the following code snippet which answers your question. Object O can hold any object. o.getClass() will return the run time class of the object
public class Main {
void method(Object o) {
System.out.println(this.getClass() == o.getClass());
}
public static void main(String[] args) {
new Main().method(new Object()); // false
new Main().method(new Main()); // true
new Main().method(new String()); // false
new Main().method(new MainOne()); // false
}
}
class MainOne extends Main
{
}
So lets say there are two classes. Class A and Class B. Class A is a super class of class B. So this method would be in class B. "this.getClass()" refers to an object of class B while o.getClass()(The super class) will refer to class A. So Class B will not equal Class A. Meaning it will go into the if statement.
Suppose your super class is SHAPE and you have a class RECT that is a subclass of SHAPE.
If the this variable is for a RECT and the Object o is also a RECT,
then line 4 will return true because they are the same class (RECT).
The two objects will be equal as long as their types are the same at runtime.
However, if Object o is of type SQUARE, which also subclasses SHAPE,
(and could even subclass RECT).
then it will not be equal to the this pointer (RECT),
because their classes are different at runtime.
Now for why this kind of type checking is bad in the equals method (specifically for the use case of Hibernate entity classes).
If you use Hibernate and you are checking a newly created object whose class type is RECT against an object whose class type was RECT at the time it was cached in Hibernate, the class of the object in the cache will actually be a sub-class of type RECT, because Hibernate does byte-code manipulation and wraps the objects in a synthetic sub-class (RECT_$$javassist).
This means that your Hibernate cached objects that you expect to be equal will never be equal.
If the object is in a child collection, Hibernate will assume you wanted to delete the old object from the collection and create the new object in the collection instead of doing a (potential) update on an existing object in the collection.
We have legacy code that did this and could never figure out why (until now) it kept doing deletes and re-inserts on our collection.
For Hibernate entity objects you should use the instanceof operator to determine if two objects could be equal - and then cast Object o and continue the comparison operation with class SHAPE specific fields.
If your subclasses should not be considered equal, then you will have to implement equals() in each subclass to check for instanceof.
For other use cases, you will have to determine if there is a chance that someone (or some other library) could sub-class your Class (even through byte code manipulation) and whether any sub-classes should still be considered equal or not.
For instance, if you do any kind of mocking in your unit tests, a bad equals method may cause otherwise equal objects to be non-equal due to their classes not being equal.
Back to the OP's code. A better way to code the equals method would be:
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof AlunoTE))
return false;
AlunoTE umAluno = (AlunoTE) o;
return(this.nomeEmpresa.equals(umAluno.getNomeEmpresa()) && super.equals(umAluno);
}
Because the instanceof operator always returns false for null, there is no need for a null check too.
Why doesn't this code compile?
public boolean isOf(Class clazz, Object obj){
if(obj instanceof clazz){
return true;
}else{
return false;
}
}
Why I can't pass a class variable to instanceof?
The instanceof operator works on reference types, like Integer, and not on objects, like new Integer(213). You probably want something like
clazz.isInstance(obj)
Side note: your code will be more concise if you write
public boolean isOf(Class clazz, Object obj){
return clazz.isInstance(obj)
}
Not really sure if you need a method anymore ,though.
instanceof can be used only with explicit class names (stated at compile time). In order to do a runtime check, you should do:
clazz.isInstance(obj)
This has a small advantage over clazz.isAssignableFrom(..) since it deals with the case obj == null better.
As others have mentioned, you cannot pass a class variable to instanceof because a class variable references an instance of an Object, while the right hand of instanceof has to be a type. That is, instanceof does not mean "y is an instance of Object x", it means "y is an instance of type X". In case you don't know the difference between an Object and a type, consider:
Object o = new Object();
Here, the type is Object, and o is a reference to the instance of the Object with that type. Thus:
if(o instanceof Object)
is valid but
if(o instanceof o)
is not because o on the right hand side is an Object, not a type.
More specific to your case, a class instance is not a type, it is an Object (which is created for you by the JVM). In your method, Class is a type, but clazz is an Object (well, a reference to an Object)
What you need is an way to compare an Object to a Class Object. It turns out that this is popular so this is provided to you as a method of the Class Object: isInstance().
Here is the Java Doc for isInstance, which explains this better:
public boolean isInstance(Object obj)
Determines if the specified Object is assignment-compatible with the
object represented by this Class. This method is the dynamic
equivalent of the Java language instanceof operator. The method
returns true if the specified Object argument is non-null and can be
cast to the reference type represented by this Class object without
raising a ClassCastException. It returns false otherwise.
Specifically, if this Class object represents a declared class, this
method returns true if the specified Object argument is an instance of
the represented class (or of any of its subclasses); it returns false
otherwise. If this Class object represents an array class, this method
returns true if the specified Object argument can be converted to an
object of the array class by an identity conversion or by a widening
reference conversion; it returns false otherwise. If this Class object
represents an interface, this method returns true if the class or any
superclass of the specified Object argument implements this interface;
it returns false otherwise. If this Class object represents a
primitive type, this method returns false.
Parameters: obj - the object to check
Returns: true if obj is an instance of this class
Since: JDK1.1
Firstly, instanceof requires that the operand on the right is an actual class (e.g. obj instanceof Object or obj instanceof Integer) and not a variable of type Class. Secondly, you have made a fairly common newbie mistake that you really should not do... the following pattern:
if ( conditional_expression ){
return true;
} else{
return false;
}
The above can be refactored into:
return conditional_expression;
You should always perform that refactoring, as it eliminates a redundant if...else statement. Similarly, the expression return conditional_expression ? true : false; is refactorable to the same result.