I want to compare the class type in Java.
I thought I could do this:
class MyObject_1 {}
class MyObject_2 extends MyObject_1 {}
public boolean function(MyObject_1 obj) {
if(obj.getClass() == MyObject_2.class) System.out.println("true");
}
I wanted to compare in case if the obj passed into the function was extended from MyObject_1 or not.
But this doesn't work. It seems like the getClass() method and the .class gives different type of information.
How can I compare two class type, without having to create another dummy object just to compare the class type?
Try this:
MyObject obj = new MyObject();
if(obj instanceof MyObject){System.out.println("true");} //true
Because of inheritance this is valid for interfaces, too:
class Animal {}
class Dog extends Animal {}
Dog obj = new Dog();
Animal animal = new Dog();
if(obj instanceof Animal){System.out.println("true");} //true
if(animal instanceof Animal){System.out.println("true");} //true
if(animal instanceof Dog){System.out.println("true");} //true
For further reading on instanceof: http://mindprod.com/jgloss/instanceof.html
If you don't want to or can't use instanceof, then compare with equals:
if(obj.getClass().equals(MyObject.class)) System.out.println("true");
BTW - it's strange because the two Class instances in your statement really should be the same, at least in your example code. They may be different if:
the classes have the same short name but are defined in different packages
the classes have the same full name but are loaded by different classloaders.
It prints true on my machine. And it should, otherwise nothing in Java would work as expected. (This is explained in the JLS: 4.3.4 When Reference Types Are the Same)
Do you have multiple classloaders in place?
Ah, and in response to this comment:
I realise I have a typo in my
question. I should be like this:
MyImplementedObject obj = new MyImplementedObject ();
if(obj.getClass() == MyObjectInterface.class) System.out.println("true");
MyImplementedObject implements
MyObjectInterface So in other words, I
am comparing it with its implemented
objects.
OK, if you want to check that you can do either:
if(MyObjectInterface.class.isAssignableFrom(obj.getClass()))
or the much more concise
if(obj instanceof MyobjectInterface)
As said earlier, your code will work unless you have the same classes loaded on two different class loaders.
This might happen in case you need multiple versions of the same class in memory at the same time, or you are doing some weird on the fly compilation stuff (as I am).
In this case, if you want to consider these as the same class (which might be reasonable depending on the case), you can match their names to compare them.
public static boolean areClassesQuiteTheSame(Class<?> c1, Class<?> c2) {
// TODO handle nulls maybe?
return c1.getCanonicalName().equals(c2.getCanonicalName());
}
Keep in mind that this comparison will do just what it does: compare class names; I don't think you will be able to cast from one version of a class to the other, and before looking into reflection, you might want to make sure there's a good reason for your classloader mess.
Comparing an object with a class using instanceOf or ... is already answered.
If you have two objects and you want to compare their types with each other, you can use:
if (obj1.getClass() == obj2.getClass()) {
// Both have the same type
}
If you had two Strings and compared them using == by calling the getClass() method on them, it would return true. What you get is a reference on the same object.
This is because they are both references on the same class object. This is true for all classes in a java application.
Java only loads the class once, so you have only one instance of a given class at a given time.
String hello = "Hello";
String world = "world";
if (hello.getClass() == world.getClass()) {
System.out.println("true");
} // prints true
Hmmm... Keep in mind that Class may or may not implement equals() -- that is not required by the spec. For instance, HP Fortify will flag myClass.equals(myOtherClass).
Check Class.java source code for equals()
public boolean equals(Object obj) {
return this == obj;
}
Related
I'm a beginner in Java programming. Currently I'm reading about Inheritance and the equals method at this page.
I understand the explanations until this point:
Compare the classes of this and otherObject. If the semantics of
equals can change in subclasses, use the getClass test:
if (getClass() != otherObject.getClass()) return false;
If the same semantics holds for all subclasses, you can use an instanceof test:
if (!(otherObject instanceof ClassName)) return false;
I don't understand what 'semantics of equals' mean. Can someone share scenarios where we use getClass() and instanceof please?
Thank you for reading.
Simply put, getClass() returns the immediate class of the object. for example,
class A { }
class B extends A { }
if we create two objects from A and B,
A objA = new A();
B objB = new B();
now we can check how getClass work
System.out.println(objA.getClass()); //Prints "class A"
System.out.println(objB.getClass()); //Prints "class B"
So,
objA.getClass() == objB.getClass()
returns false. But
System.out.println(objB instanceof A); //Prints true
This is because instanceof returns true even if a superclass is given of the provided object.
So, when you design your equals() method, if you want to check the given object(otherObject) is instantiated from the same immediate Class, use the
if (getClass() != otherObject.getClass()) return false;
If it is okay that the given object(otherObject) is made even from a subclass of a Class (ClassName) you provide, use
if (!(otherObject instanceof ClassName)) return false;
Simply, "semantics of equals" means "The purpose you expect from equals() method". So you can use the appropriate method according to your need.
changing semantics of equals
It means that may be the equals method can be overwritten in subclasses, in such scenarios use getClass to check whether objects belong to same class.
In general, getClass vs instance of
We use getClass when we need to know the class of a particular object. Consider cases when there is linear chain of Inheritance as follows:
MainClass -> SubClassLevel1 -> SubClassLevel2
and initialize as
MainClass mc = new SubClassLevel2();
Here to know that which class in the hierarchy is the class of our object.
Now, instance of method is used in the cases where you have to merely check that whether x is an instance of class Y or not. It returns boolean.
Hope this answers your query :)
Why is second SOP showing output as true here, I was hoping it would display false like first SOP ?
public class reflect1 {
public static void main(String[] args) {
Reflect1A obj1 = new Reflect1A();
Reflect1A obj2 = new Reflect1A();
System.out.println(obj1 == obj2);
Class c1 = obj1.getClass();
Class c2 = obj2.getClass();
System.out.println(c1 == c2);
}
}
class Reflect1A {
}
From the Java Language Specification
The method getClass returns the Class object that represents the class of the object.
A Class object exists for each reference type. It can be used, for
example, to discover the fully qualified name of a class, its members,
its immediate superclass, and any interfaces that it implements.
Since both your objects are of type Reflect1A, they both return the same Class object.
You would get the same object by doing
Class<?> clazz = Class.forName("com.example.Reflect1A")
System.out.println(c1 == clazz); // true
(though this is not necessarily required by all classloaders.)
The values of obj1 and obj2 refer to different objects - when you use == in Java and both operands are references, the result is to compare whether those references refer to the exact same object. In this case you've got two different objects, so the references are not the same.
However, they're both of the same class, so that's why c1 == c2 is true.
The first line prints false because it is a different instance of the same class.
The second line prints true because it is the same class type. There is a obscure gotcha here to be aware of, if you're in a multiple classloader environment, e.g. an application server like JBoss, or OSGI etc, it is possible for two class instances to not be equal
An object is equal (==) only to itself. So clearly both getClass() statements are returning the same Class object
Because obj1 and obj2 are different instances (first check is false) of the same type (class - second check is true).
https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#getClass%28%29
why did you expect to return false?
The operator == compares the references in which the objects are created by default
Obj1 and Obj2 are of same type class Reflect1. For these objects are equal only when compared like this obj1.equal(obj2).
While the class type of obj1 and obj2 are the same == operation will be true.
It is stated in Object's .equals(Object) javadoc:
It is symmetric: for any non-null reference values x and y,
x.equals(y) should return true if and only if y.equals(x) returns
true.
Almost everywhere in example code I see overridden .equals(Object) method which uses instanceof as one of the first tests, for example here: What issues / pitfalls must be considered when overriding equals and hashCode?
public class Person {
private String name;
private int age;
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!(obj instanceof Person))
return false;
...
}
}
Now with class SpecialPerson extends Person having in equals:
if (!(obj instanceof SpecialPerson))
return false;
we con not guarantee that .equals() is symmetric.
It has been discussed for example here: any-reason-to-prefer-getclass-over-instanceof-when-generating-equals
Person a = new Person(), b = new SpecialPerson();
a.equals(b); //sometimes true, since b instanceof Person
b.equals(a); //always false
Maybe I should add in the beginning of SpecialPerson's equals direct call to super?
public boolean equals(Object obj) {
if( !obj instanceof SpecialPerson )
return super.equals(obj);
...
/* more equality tests here */
}
A lot of the examples use instanceof for two reasons: a) it folds the null check and type check into one or b) the example is for Hibernate or some other code-rewriting framework.
The "correct" (as per the JavaDoc) solution is to use this.getClass() == obj.getClass(). This works for Java because classes are singletons and the VM guarantees this. If you're paranoid, you can use this.getClass().equals(obj.getClass()) but the two are really equivalent.
This works most of the time. But sometimes, Java frameworks need to do "clever" things with the byte code. This usually means they create a subtype automatically. Since the subtype should be considered equal to the original type, equals() must be implemented in the "wrong" way but this doesn't matter since at runtime, the subtypes will all follow certain patterns. For example, they will do additional stuff before a setter is being called. This has no effect on the "equalness".
As you noticed, things start to get ugly when you have both cases: You really extend the base types and you mix that with automatic subtype generation. If you do that, you must make sure that you never use non-leaf types.
You are missing something here. I will try to highlight this:
Suppose you have Person person = new Person() and Person personSpecial = new SpecialPerson() then I am sure you would not like these two objects to be equal. So, its really working as required, the equal must return false.
Moreover, symmetry specifies that the equals() method in both the classes must obey it at the same time. If one equals return true and other return false, then I would say the flaw is in the equals overriding.
Your attempt at solving the problem is not correct. Suppose you have 2 subclasss SpecialPerson and BizarrePerson. With this implementation, BizarrePerson instances could be equal to SpecialPerson instances. You generally don't want that.
don't use instanceof. use this.getClass() == obj.getClass() instead. then you are checking for this exact class.
when working with equalsyou should always use the hashCode and override that too!
the hashCode method for Person could look like this:
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
and use it like this in your equals method:
if (this.hashCode() != obj.hashCode())
{
return false;
}
A type should not consider itself equal to an object of any other type--even a subtype--unless both objects derive from a common class whose contract specifies how descendants of different types should check for equality.
For example, an abstract class StringyThing could encapsulate strings, and provide methods to do things like convert to a string or extract substrings, but not impose any requirements on the backing format. One possible subtype of StringyThing, for example, might contain an array of StringyThing and encapsulate the value of the concatenation of all those strings. Two instances of StringyThing would be defined as equal if conversion to strings would yield identical results, and comparison between two otherwise-indistinguishable StringyThing instances whose types knew nothing about each other may have to fall back on that, but StringyThing-derived types could include code to optimize various cases. For example, if one StringyThing represents "M repetitions of character ch" and another represents "N repetitions of the string St", and the latter type knows about the first, it could check whether St contains nothing but M/N repetitions of the character ch. Such a check would indicate whether or not the strings are equal, without having to "expand out" either one of them.
Is the following code valid?
void myMethod (Class classType) {
if (classType == MyClass.class) {
// do something
}
}
myMethod (OtherClass.class);
If not is there any other approach where I can check if a passed .class (Class Type) is of type - MyClass ?
Yes, that code is valid - if the two classes have been loaded by the same classloader. If you want the two classes to be treated as equal even if they've been loaded by different classloaders, possibly from different locations, based on the fully-qualified name, then just compare fully-qualified names instead.
Note that your code only considers an exact match, however - it won't provide the sort of "assignment compatibility" that (say) instanceof does when seeing whether a value refers to an object which is an instance of a given class. For that, you'd want to look at Class.isAssignableFrom.
I'd rather compare the canonical names to be completely sure, classType.getCanonicalName().equals(MyClass.class.getCanonicalName()).
Note that this may bring issues with anonymous and inner classes, if you are using them you may consider using getName instead.
That worked for me:
public class Test {
void myMethod(Class classType) {
System.out.println(classType.isAssignableFrom(Test.class));
}
public static void main(String[] args) {
Test t = new Test();
t.myMethod(String.class);
}
}
I think you are looking for instanceof.
Animal a = new Tiger();
System.out.println(a instanceof Tiger); // true
System.out.println(a instanceof Animal); //true
Alternatively you could compare two classes with
a.getClass() == b.getClass()
Don't use
classType.getCanonicalName().equals(MyClass.class.getCanonicalName())
the above will not consider any generics (all map are the same, all set are the same etc)
I'm going through a book on data structures. Currently I'm on graphs, and the below code is for the vertex part of the graph.
class Vertex<E>{
//bunch of methods
public boolean equals(Object o){
//some code
}
}
When I try to implement this equals method my compiler complains about not checking the type of the parameter and just allowing any object to be sent it. It also does seem a bit strange to me why that parameter shouldn't be a Vertex instead of an Object. Is there a reason why the author does this or is this some mistake or antiquated example?
#Override
public boolean equals(Object obj)
{
if (!(obj instanceof Vertex)) return false;
else return // blah blah
}
equals(Object) is the method defined in the root - Object. If you don't match the signature exactly, Object's version will be called when someone checks if two objects are equal. Not what you want.
You've probably seen other methods (like Comparator) where you can use the exact time. That's because those APIs were generic-ified with Java 5. Equals can't be because it is valid to call equals with two separate types. It should return false, but it is valid.
equals is a method inherited from Object, is defined to be flexible enough so that you can take any object and test if it is equal to any other object (as it rightfully should be able to do), so how could it be any other way?
Edit 1
Comment from jhlu87:
so is it not good form to write an equals method that has an input parameter of vertex?
You are welcome to create your own overload to any method, including equals, but doing so without changing the name could risk confusing many who would assume that your equals is the one that inherits from Object. If it were my code and I wanted a more specific equals method, I'd name it slightly different from just "equals" just to avoid confusion.
If your method doesn't take an argument of type Object, it isn't overriding the default version of equals but rather overloading it. When this happens, both versions exist and Java decides which one to use based on the variable type (not the actual object type) of the argument. Thus, this program:
public class Thing {
private int x;
public Thing(int x) {
this.x = x;
}
public boolean equals(Thing that) {
return this.x == that.x;
}
public static void main(String[] args) {
Thing a = new Thing(1);
Thing b = new Thing(1);
Object c = new Thing(1);
System.out.println(a.equals(b));
System.out.println(a.equals(c));
}
}
confusingly prints true for the first comparison (because b is of type Thing) and false for the second (because c is of type Object, even though it happens to contain a Thing).
It's because this method existed before generics, so for backward compatabitity it has to stay this way.
The standard workaround to impose type is:
return obj instanceof MyClass && <some condition>;
It is because the author is overriding equals. Equals is specified in java.lang.Object and is something that all classes inherrits from.
See the javadoc for java.lang.Object