Inheritance and 'Instanceof' testing result - java

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.

Related

Java getClass and super classes

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.

How exactly does the instanceof method work? and why is it not working in my code shown below? [duplicate]

This question already has answers here:
instanceof - incompatible conditional operand types
(3 answers)
Closed 8 years ago.
So i have defined the main class to be shown below and i have defined a words class and a sentence class. Note the program should return false when ran. However, i am getting a "Incompatible conditional operand types words and sentence" error when i run it. Isn't this how the instanceof operator is to be used or am i confused? How can my program be modified to run without crashing?
public class main{
public static void main (String[] args){
words example = new words("heyo");
sentence ex = new sentence("wats up dog");
System.out.println(example instanceof sentence);
}
}
If the variable you are running instanceof on can't be of the same type as the class you supply to the operator, the code won't compile.
Since example is of type words, which is not a sub-class or super-class of sentence, instanceof cannot be applied on example and sentence.
Here's what JLS 15.20.2 says about the expression "RelationalExpression instanceof ReferenceType":
If a cast of the RelationalExpression to the ReferenceType would be rejected as a compile-time error, then the instanceof relational expression likewise produces a compile-time error. In such a situation, the result of the instanceof expression could never be true.
At run time, the result of the instanceof operator is true if the value of the RelationalExpression is not null and the reference could be cast (§15.16) to the ReferenceType without raising a ClassCastException. Otherwise the result is false.
Your class i.e. sentence should be in the same hierarchy as your example is from (i.e. type or subtype of word) for this to compile. Here are the examples of the same and this is hwat it says about instanceof:
The instanceof operator compares an object to a specified type. You can use it to test if an object is an instance of a class, an instance of a subclass, or an instance of a class that implements a particular interface.
If words is not a subclass of sentence class, there is no way an example object is instance of sentence.
Instance of method is applicable only for class in same hierachy. For example :-
Class A{
}
Class B extends A {
}
Class C{
}
// in your main
A a = new B();
// then you can check if a is object of B
System.out.println(a instanceOf B);
//You cannot do
System.out.println(a instance of C);
// as no way you cannot do this
A a = new C();
In the instanceof operator, if the LHS (instance) is not an instance of the RHS (Class) then it returns false and vice-versa. However, in your case, it is trivial for the compiler to resolve that these are incompatible types so it complains.
So something like the following (similar to your example):
String a = "asd";
Integer b = new Integer(12);
System.out.println(""+ (b instanceof String)); // compile time error
will never compile. However if the compiler cannot determine as in the following case then when you run it would return false:
public static void main(String[] args) {
String a = "asd";
Integer b = new Integer(12);
check (b); // prints false
}
public static void check(Object o) {
System.out.println(o instanceof String);
}

Compilation error with == operator

I've isolated the error to this line:
string.getClass() == jojo.getClass()
Shouldn't this line create two Class objects and then check if they (as in the two references) point to the same object? Rather than returning a value of false, the code won't run.
public class Tester
{
public static void main(String[] args)
{
OreoJar jojo = new OreoJar(0);
OreoJar momo = new OreoJar(1);
String string = "Hello";
if (momo.getClass() == jojo.getClass())
{
System.out.println("Momo and jojo are of the same class");
}
if (string.getClass() == jojo.getClass())
{
System.out.println("String and jojo are of the same class");
}
}
}
public class OreoJar
{
int oreos;
public OreoJar(int oreos)
{
this.oreos = oreos;
}
public void count()
{
System.out.println(oreos + " oreos in this jar!");
}
}
This comment is kind of hidden and I think its worth mentioning since it makes the most sense to a beginner (such as myself)
-According to the JLS "It is a compile-time error if it is impossible to convert the type of either operand to the type of the other by a casting conversion" so two references of types A and B can be compared if, and only if, either A can be cast to B or B can be cast to A. – Patricia Shanahan
I agree OP should quote the compilation error.
Anyway the compilation error is quite obvious when anyone actually does a compilation.
The error is:
Tester.java:15: incomparable types: java.lang.Class<capture#125 of ? extends java.lang.String> and java.lang.Class<capture#29 of ? extends OreoJar>
if (string.getClass() == jojo.getClass()){
^
Reason seems obvious.
From Javadoc of Object.getClass():
The java.lang.Class object that represents the runtime class of the
object. The result is of type Class<? extends X> where X is the
erasure of the static type of the expression on which getClass is
called.
That means, an String instance is going to return a reference to Class<? extends String>, while an OreoJar instance is going to return reference to Class<? extends OreoJar>
The two types are simply not compatible, as the compiler knows that there is no chance that any type that extends String can be a type extends OreoJar. So comparison is going to cause compilation error.
A bit off topic but I think worth mentioning, you said:
Shouldn't this line create two Class objects and then check if they point to the same object
I think it is better to have clearer understanding. It is not going to "create" two Class objects. getClass() is going to return you a reference to Class object. And, it is always a reference that can point to an object, not object that point to object (it sounds weird too)
I think the reason it won't compile is due to the fact that Class has generic component. Try using momo.getClass().equals(jojo.getClass())
And you might also try comparing the canonical names of the classes for a similar effect: momo.getClass().getCanonicalName().equals(jojo.getClass().getCanonicalName())
getClass() returns an instance of a Class. getClass().getName() returns a string. The String.equals(otherString) method is the correct way to compare Strings for equality.

Why cast after an instanceOf?

In the example below (from my coursepack), we want to give to the Square instance c1 the reference of some other object p1, but only if those 2 are of compatible types.
if (p1 instanceof Square) {c1 = (Square) p1;}
What I don't understand here is that we first check that p1 is indeed a Square, and then we still cast it. If it's a Square, why cast?
I suspect the answer lies in the distinction between apparent and actual types, but I'm confused nonetheless...
Edit:
How would the compiler deal with:
if (p1 instanceof Square) {c1 = p1;}
Edit2:
Is the issue that instanceof checks for the actual type rather than the apparent type? And then that the cast changes the apparent type?
Old code will not work correctly
The implied cast feature is justified after all but we have trouble to implement this FR to java because of backward-compatibility.
See this:
public class A {
public static void draw(Square s){...} // with implied cast
public static void draw(Object o){...} // without implied cast
public static void main(String[] args) {
final Object foo = new Square();
if (foo instanceof Square) {
draw(foo);
}
}
}
The current JDK would compile the usage of the second declared method.
If we implement this FR in java, it would compile to use the first method!
🔴 JDK 14
We finally implemented this feature in JDK 14. As you might have noticed you can declare a new variable within the instanceof-linkage. This new variable has been defined by the value of a automatically downcast to the specified type.
if (any instanceof String s) {
System.out.println(s);
}
Keep in mind, you could always assign an instance of Square to a type higher up the inheritance chain. You may then want to cast the less specific type to the more specific type, in which case you need to be sure that your cast is valid:
Object p1 = new Square();
Square c1;
if(p1 instanceof Square)
c1 = (Square) p1;
The compiler does not infer that since you are in the block, you have done a successful check for the type of the object. An explicit cast is still required to tell the compiler that you wish to reference the object as a different type.
if (p1 instanceof Square) {
// if we are in here, we (programmer) know it's an instance of Square
// Here, we explicitly tell the compiler that p1 is a Square
c1 = (Square) p1;
}
In C# you can do the check and the cast in 1 call:
c1 = p1 as Square;
This will cast p1 to a Square, and if the cast fails, c1 will be set to null.
Just to provide an update on this, Java 14 now provides pattern matching for instanceof, this allows you to check and cast in one fell swoop.
This (old way):
void outputValue(Object obj) {
if (obj instanceof String) { // Compare
String aString = (String) obj; // New variable & explicit casting
System.out.println(aString.toUpperCase()); // Access member
}
}
Can be simplified to this:
void outputValue(Object obj) {
if (obj instanceof String aString) { // Compare and cast (if true)
System.out.println(aString.toUpperCase()); // Access member
}
}
There's a difference between measuring if some object will fit in a box, and actually putting it in the box. instanceof is the former, and casting is the latter.
Because this particular syntactic sugar is not yet added to the language. I think it was proposed for Java 7, but it doesn't seem to have entered project coin
E.g. If you hand over p1 as of type Object, the compiler wouldn't know that it is in fact an instance of Square, so that Methods etc. wouldn't be accessible. The if simply checks for a certain type to return true/false, but that doesn't change the type of the variable p1.
The test is done to prevent from ClassCastExceptions at runtime:
Square c1 = null;
if (p1 instanceof Square) {
c1 = (Square) p1;
} else {
// we have a p1 that is not a subclass of Square
}
If you're absolutly positive that p1 is a Square, then you don't have to test. But leave this to private methods...
The variable p1 has whatever type it started with - let's say Shape. p1 is a Shape, and only a Shape, no matter that its current contents happen to be a Square. You can call - let's say - side() on a Square, but not on a Shape. So long as you are identifying the entity in question via the variable p1, whose type is Shape, you can't call side() on it, because of the type of the variable. The way Java's type system works, if you can call p1.side() when you happen to know it's a Square, you can always call p1.side(). But p1 can hold not just Square Shapes, but also (say) Circle Shapes, and it would be an error to call p1.side() when p1 held a Circle. So you need another variable to represent the Shape which you happen to know is a Square, a variable whose type is Square. That's why the cast is necessary.
Not to be obnoxious, but you have to tell the compiler what you want to do because the alternative would be for it to guess what you're trying to do. Sure, you might think, "If I'm checking the type of an object, OBVIOUSLY that must mean that I want to cast it to that type." But who says? Maybe that's what you're up to and maybe it isn't.
Sure, in a simple case like
if (x instanceof Integer)
{
Integer ix=(Integer) x;
...
My intent is pretty obvious. Or is it? Maybe what I really want is:
if (x instanceof Integer || x instanceof Double)
{
Number n=(Number) x;
... work with n ...
Or what if I wrote:
if (x instanceof Integer || x instanceof String)
What would you expect the compiler to do next? What type should it assume for x?
RE the comments that instanceof is obsolete or otherwise a bad idea: It can certainly be mis-used. I recently worked on a program where the original author created six classes that all turned out to be pages and pages long, but identical to each other, and the only apparent reason for having them was so he could say "x instanceof classA" versus "x instanceof classB", etc. That is, he used the class as a type flag. It would have been better to just have one class and add an enum for the various types. But there are also plenty of very good uses. Perhaps the most obvious is something like:
public MyClass
{
int foo;
String bar;
public boolean equals(Object othat)
{
if (!(othat instanceof MyClass))
return false;
MyClass that=(MyClass) othat;
return this.foo==that.foo && this.bar.equals(that.bar);
}
... etc ...
}
How would you do that without using instanceof? You could make the parameter be of type MyClass instead of Object. But then there's be no way to even call it with a generic Object, which could be highly desirable in many cases. Indeed, maybe I want a collection to include, say, both Strings and Integers, and I want comparisons of unlike types to simply return false.
As Leroy mentioned, Java 14 introduces pattern matching for instanceof. So, you can combine both instanceof check and typecast altogether in a single expression:
if (p1 instanceof Square) {
c1 = (Square) p1;
}
can be rewritten as
if (p1 instanceof Square c1) {
// use c1
}
This feature is finalized in Java 16 (JEP 394). For the below versions, refer this link to enable this preview feature from IDEs such as IntelliJ, Eclipse, and STS.
If c1 is declared as a type of Square then casting is required. If it is a declared as an Object then casting is not needed.

Why can't a "Class" variable be passed to instanceof?

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.

Categories

Resources