Java Reflect API - isAssignableFrom, Extends/Implements - java

In Java Reflect API, in .isAssignableFrom methods in Class has its javadocs saying that it will return true if "the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter".
The question is whether .isAssignableFrom will return true if on a class that implements Interface, or it only returns true when "extends" is used?
In other words, what will happen and why in case:
public class MyClass implements MyInterface{}
MyInterface.isAssignable(MyClass.class) == false/true ?

It returns true.
That are two ways to tell this from the Javadoc:
the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter
The term superinterface is defined in the Java Language Specification as follows:
The optional implements clause in a class declaration lists the names of interfaces that are direct superinterfaces of the class being declared.
Therefore, MyInterface is a superinterface of MyClass, and therefore MyInterface.class.isAssignableFrom(MyClass.class) is true.
The other way to tell is the next paragraph of the javadoc:
Specifically, this method tests whether the type represented by the
specified Class parameter can be converted to the type
represented by this Class object via an identity conversion
or via a widening reference conversion. See The Java Language
Specification, sections 5.1.1 and 5.1.4 , for details.
which matches because
MyInterface i = new MyClass();
compiles.

Related

Doing equivalent test between superclass and subclass without instantiating the subclass

Just playing with java fundamentals I came up with question whether I can check inheritance between two .class objects without instantiating them.
For example, if I have a class Foo, which is a subclass of Baa:
Foo a = new Foo(); return a instanceof Baa will return true.
But what do I have to do if I wanna do the equivalent test with Foo.class and Baa.class?
I think I can do something like this:
Foo.class.newInstance() instanceof Baa.class
But would it be possible to test the same without instantiating Foo?
You can use Class.isAssignableFrom(Class) to check the hierarchy in inheritance. According to java-docs:
Determines if the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter.
This will not instantiate any of the classes and you will get the desired functionality.

What makes an object assignment-compatible with another class?

I'm wondering what specifically allows an object of one class to be cast as another class. Looking at the Class.isInstance(Object obj) javadoc, it suggests that an object has to be 'assignment-compatible' with another class in order to be cast to that class. But what constitutes 'assignment-compatible'?
I'm trying to figure out how the following returned a ClassCastException:
public class A
{
multiple private attributes
No constructor
multiple public methods
}
public class B extends A
{
blank default constructor
2 additional private attributes
4 additional public getter / setter methods for these attributes
}
The line
B b = (B)variable.getA() // where getA() returned an instance of class A
returns a ClassCastException A cannot be cast as B. I know that casting from a parent class to a subclass is a bad idea. I did not write the code in question, I'm just looking at it in a production support capacity.
Class B is the only class in the codebase that extends from A. So the result of getA() is not an object of another subclass of A (say C) cast to A.
So why in this instance can Java not cast an object of class A to the seemingly compatible class B?
Consider these two cases:
A a = new B();
B b = (B)a; <-- Ok.
A a = new A();
B b = (B)a; <-- ClassCastException
So in order to cast an object to B it must be an instance of B (or a subclass of B).
In your case it is however an instance of A.
The javadoc for Class.isInstance(Object obj) gives the definition of assignment compatible:
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.
Basically, you can assign an object of type A to variable of type B if type A extends or implements type B.
Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance. For example, the following code generates a ClassCastException:
Object x = new Integer(0);
System.out.println((String)x);
more info here and here.
If we replaced A and B with meaningful names that help us think about the problem, then the answer becomes clearer. If A becomes Mammal and B is Dog, then we can reasonably say that all Dogs are Mammals, but we cannot say that all Mammals are Dogs. Even if they superficially shared the same attributes, there is no guarantee that all Mammals would fulfill the contract of being a Dog, and the compiler shouldn't try to assume so.
Just wanted to add the official specification to support
Ricardo's correct answer that "you can assign an object of type A to variable of type B if type A extends or implements type B":
The JLS defines assignment-compatibility as follows:
5.2. Assignment Contexts
If the type of an expression can be converted to the type of a variable by assignment conversion, we say the expression (or its value) is assignable to the variable or, equivalently, that the type of the expression is assignment compatible with the type of the variable.
The term "assingment conversion" is only defined as applying the appropriate conversion from the list given in the "Assignment Contexts"-chapter:
The term "conversion" is also used to describe, without being specific, any conversions allowed in a particular context. For example, we say that an expression that is the initializer of a local variable is subject to "assignment conversion", meaning that a specific conversion will be implicitly chosen for that expression according to the rules for the assignment context.
The most relevant for reference types being
5.1.5. Widening Reference Conversion
A widening reference conversion exists from any reference type S to any reference type T, provided S is a subtype of T (§4.10).
Subtypes include implemented interfaces (see 4.10.2. Subtyping among Class and Interface Types ).
There are additional rules for numeric and generic types, but they are not relevant for the example given in the question.

Where is ObjectClass.class variable defined/initialized?

Consider sample code below
public class Test {
public static void main(String args[]) {
Test t = new Test();
Class c2 = Test.class;
System.out.println(c2);
}
}
Test.class statically evaluates and returns compile time Class object. Looking at the Test.class syntax it looks like the variable class is of type java.lang.Class and is static and public. My question is where is this variable defined? It is not present in Test class (because I don't declare it) neither it is in the java.lang.Object class.
I saw an analogous method public final native Class<?> getClass();. This is present in java.lang.Object and is a native java method. This method returns the runtime Class of an object.
So my question is where is this public & static class variable defined?(Please correct me if I have mistaken) Is it again some native implementation? This is set at compile time and being static needs no class instance to be created. So if even this is some native implementation is it initialized by registerNatives() method in java.lang.Object?
These are called class literals and are defined by the language itself as per JLS §15.8.2 (there is no "class member"):
A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a '.' and the token class.
The type of C.class, where C is the name of a class, interface, or array type (§4.3), is Class<C>.
The type of p.class, where p is the name of a primitive type (§4.2), is Class<B>, where B is the type of an expression of type p after boxing conversion (§5.1.7).
The type of void.class (§8.4.5) is Class<Void>.
One indication that these constructs are intrinsically built into the language is that they even work with primitives!
System.out.println(int.class);
System.out.println(double.class);
// etc.
class is not normal static variable. It's a language construct which is replaced at compilation time.
Because class is a keyword it wouldn't even be possible to declare a variable with that name.
Your assumption that class is a static field of class Class is not exact. Assume that this is correct. In this case the value of this field will be exactly the same for all classes that is wrong.
Although MyClass.class syntactically looks like access to static field it is just a special syntax of language. Think about this as a kind of operator.
Probably JVM creates some kind of synthetic class that wraps real class and has such field but it is just an assumption about internal representation of classes in JVM.

A method for obtaining an object of type Class

In my textbook I can read:
If T is any Java type, then T.class is the matching class object. For example:
Class cl1 = Date.class; // if you import java.util.*;
Class cl2 = int.class;
Class cl3 = Double[].class;
Later on I'm reading:
The virtual machine manages a unique Class object for each type. Therefore, you can use the == operator to compare class objects. For example:
if (e.getClass() == Employee.class)
Could you help me find anything about this .class field in the documentation.
And another qutstion - I can't understand whether the e.getClass() == Employee.class is the same as e.class == Employee.class. I mean, if it is the same, why the author of the textbook used getClass here in the lefthand expression.
From JLS §15.8.2 - Class Literals:
A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a '.' and the token class.
The type of C.class, where C is the name of a class, interface, or array type (§4.3), is Class.
So, Date.class, int.class are nothing but class literals, which give appropriate Class objects for a class type.
I can't understand whether the e.getClass() == Employee.class is the same as e.class == Employee.class
No, they are not the same. In fact, e.class won't even compile. As per the definition of class literal above, since e is not a type but an object of Employee (I assume that), e.class is not a valid class literal. To get the Class object of a class, using it's instance, you need to use Object#getClass() method.
So, e.getClass() and Employee.class are two different ways to obtain the Class object for Employee class. Both to be used in different circumstances. When you know the class type, use 2nd version, and when you have an instance of your class, use the 1st version.
However, note that in case of inheritance, e.getClass() might not return the same Class object as Employee.class. The former would return the Class object of the actual subclass object, referred by the reference e, whereas the later would always give you Class<Employee>.
If you neither have the instance, nor the class type available, then you can also get the Class object for a class name in String form, using - Class#forName(String) method.
How you get a Class object depends on what you already know. If you have an object referenced by x, you can obtain the Class object for its class by x.getClass(). If you know, when you are writing your code, the name of a type T, you can use T.class to get the class object. There is a third approach, less convenient, that only requires run time access to the class name.
The getClass method is described as one of the Object methods, in the Object API documentation.
Class literals, the T.class form, are described in the Java Language Specification.
The third approach uses one of the static forName methods defined the API documentation for java.lang.Class.

Java Object be super of my class?

I'm new on Java, reading the Sun basic tutorial and see "The Object class, defined in the java.lang package, defines and implements behavior common to all classes—including the ones that you write. " I wonder how can Object be the root parent of the class I defined if my class did not inherit from other classes.
If you continued reading on the same page
(emphasis mine):
Excepting Object, which has no superclass, every class has one and only one direct superclass (single inheritance). In the absence of any other explicit superclass, every class is implicitly a subclass of Object.
This is an implicit behavior. All of your classes extend Object (directly or not).
public class MyClass is equivalent to public class Myclass extends Object
That's the "magic" done by the Java compiler: when you write
public class MyClass {
...
}
Java compiler sees it as
public class MyClass extends java.lang.Object {
...
}
It works that way because the Java Language Specification (specifically section 8.1.4) says so:
Given a (possibly generic) class declaration for C<F1,...,Fn> (n ≥ 0, C ≠ Object), the direct superclass of the class type C<F1,...,Fn> is the type given in the extends clause of the declaration of C if an extends clause is present, or Object otherwise.

Categories

Resources