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.
Related
I've been doing the MOOC Java programming course and they typically override equals like this:
If addresses of the 2 objects are the same = return true.
If compared object is not an instance of the same class as the first object = return false.
Convert the object to the same type as the one being compared to
Compare the variables of each object - returns true or false
I'm just a bit confused about step 2 and 3.
Step 2 checks if the 2nd object is an instance of the same type as the one its compared to...
If it isn't, the method ignores the rest of the code and returns false
If it is, we convert it to that Class. So at this point i'm confused about why we need to convert an object that is already an instance of the same class - to that class? If Java recognises it is of the same type, why do you need to convert it?
*Example below:
public boolean equals(Object comparedObject) {
// if the variables are located in the same place, they're the same
if (this == comparedObject) {
return true;
}
// if comparedObject is not of type Book, the objects aren't the same **
if (!(comparedObject instanceof Book)) {
return false;
}
// convert the object to a Book object
Book comparedBook = (Book) comparedObject;**
// if the instance variables of the objects are the same, so are the objects
if (this.name.equals(comparedBook.name) &&
this.published == comparedBook.published &&
this.content.equals(comparedBook.content)) {
return true;
}
// otherwise, the objects aren't the same
return false;
}
Because you’ve only compared the dynamic type; the static type of comparedObject is unchanged: it’s Object, and you cannot access its Book members (think about it: at this point you know that the dynamic type of comparedObject is Book; but the Java compiler still sees it with its declared type). To make the compiler understand that the object type is Book, you first need to create an object whose static type is Book, and to do that you need a cast.
The static type information is what the Java compiler sees, and which is what the compiler uses to check whether your code is statically correct.
Java is statically typed, so if the variable is declared as Object it will not let you access any fields or call any methods of Book. In this case, it is easily proven that the object can only be a Book, but the compiler does not make any attempt to prove it. (And, in a more complicated example, it couldn't.)
In the newest versions of Java, there is a shortcut you can take with the syntax:
if (comparedObject instanceof Book book) {
// do whatever with book
return this.title.equals(book.title) && this.author.equals(book.author);
}
But in earlier versions, you simply have to downcast the instance:
if (comparedObject instanceof Book) {
Book book = (Book) comparedObject;
return this.title.equals(book.title) && this.author.equals(book.author);
}
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.
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.
I'm studying 'instanceof' java, but I couldn't understand 'instanceof' clearly, I thought below answer would be true and false, but result is both true. Could you explain why this result happen? As I know, when A is child of B (Parent), and a instanceof B is 'false' but result is different with what I thought.
class Car{
String color;
int door;
}
class FireEngine extends Car{
void water(){
System.out.println("water");
}
}
public class Operator {
public static void main(String[] args) {
Car car = new FireEngine();
FireEngine fireCar = new FireEngine();
System.out.println(car instanceof FireEngine);
System.out.println(fireCar instanceof Car);
}
}
Declaration != Value
You declare car as an Car, but the Value is an FireEngine.
instanceof works based on values, not on the declarations of their variables!!!
Shortening may help to understand:
System.out.println(new FireEngine() instanceof FireEngine); // true
System.out.println(new FireEngine() instanceof Car); // true
The output of instanceof depends on the runtime type of the variable whose type you are testing. The compile time type of the variable doesn't matter (as long as it is possible that x instanceof Y will return true for some value of x, otherwise the expression won't pass compilation).
Both car and fireCar hold instances of FireEngine in your code. And since FireEngine is a kind of a Car, both car and fireCar are instanceof both Car and FireEngine, so your code prints true and true.
Implementation of the Instanceof operator. Returns a Boolean if the Object parameter (which can be an expression) is an instance of a class type.
Input 1: An object or Expression returning an object.
Input 2: A Class or an Expression returning a Class
Returns: A Boolean that is the result of testing the object against the Class.
For more information please go throught the javadocs # http://docs.oracle.com/cd/E13155_01/wlp/docs103/javadoc/com/bea/p13n/expression/operator/Instanceof.html
For more detailed explanation with examples please go through the following web page : http://mindprod.com/jgloss/instanceof.html
In Java there are two types of bindings: static (the reference type)
and dynamic (the object type).
In your case:
Car car = new FireEngine();
Car is the static type and FireEngine is dynamic type. It means, you are actually working with a FireEngine (the object type). You can imagine Car as a special pointer with a car shape pointing to the real object that is your awesome FireEngine. If you read 'instanceof' you can understand it, this method tell you if an object is an instance of a class, not the reference. So the compiler will see: FireEngine (car) instanceOf FireEngine? Of course, let's return a true!
You can have a look to this post also: What is the 'instanceof' operator used for?
The statement
As I know, when A is child of B (Parent),
and a instanceof B is 'false' but result is different with what I thought.
is not correct. instanceof does not check for the child, it tests for the parent.
Suppose there is the following code:
#SuppressWarnings("unchecked")
public static <T> T implicitCaster(Class<T> cls, Object o) {
return (T) o;
}
public static <T> T reflectionCaster(Class<T> cls, Object o) {
return cls.cast(o);
}
The code works as expected in both cases with the following exception, found in primitives:
public static void main(String[] args) {
System.out.println(implicitCaster(int.class, 42));
System.out.println(reflectionCaster(int.class, 42));
}
The first call works as expected but the second call throws java.lang.ClassCastException.
Is this a corner case in which autoboxing was disregarded? Or is it impossible to provide autoboxing in this case, of reflection casting?
Or is there something else causing this inconsistency?
Edit: calling this code works as expected:
public static void main(String[] args) {
System.out.println(implicitCaster(Integer.class, 42));
System.out.println(reflectionCaster(Integer.class, 42));
}
This happens because of type erasure.
At runtime, generic type parameters don't exist.
Casting an object to a generic type parameter has no effect. (which is why you get the unchecked cast warning)
Therefore, your first line autoboxes 42 to Object to pass to the method.
The function then returns that Object, which is passed to System.out.println.
Your second call calls the cast method of the int primitive type.
This throws an exception, because objects cannot be casted to primitive types. (auto-boxing is a purely compile-time feature, so it doesn't help)
The error happens when cast() checks isInstance() to verify that the cast is valid.
The docs for isInstance() say:
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.
(emphasis added)
Your edit works because you are no longer using a primitive type.
In both cases, the compiler autoboxes 42 so that it can be passed as an object.
The first call, as before, has no effect.
The second call verifies that the boxed integer is in fact an instance of the Integer class, then returns it.