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.
Related
I am a programmer who is getting to grasps with polymorphism after a long break and I was wondering if the following is possible. Say I had a super class in which there were some instance variables A, B and C. In all subclasses A and B are strings and behave as such in all subclasses however in all of the subclasses, the type of C may depend on the state of the subclass. I was wondering if it is possible to set C as type 'Object' in the superclass and then specify its type in each subclass using wrapper classes. e.g
public class SuperClass {
String A;
String B;
Object C;
public SuperClass(){}
}
}
public class SubClassA extends SuperClass {
public SubClassA () {
C = new String(); //notice this type is different from its type in the next class
}
}
public class SubClassB extends SuperClass {
public SubClassB () {
C = new Integer();
}
}
your thoughts would be appreciated :)
Thanks
No. "Set an object to a specific type" does not mean anything. In fact, "Set an object." does not mean anything.
A, B, and C in your example are not objects, they are variables. There are three kinds of variable in Java; primitives (e.g, int, boolean, double), array reference variables, and object reference variables. A, B, and C are object reference variables. That means, A, B, and C each hold the identity of some object.
Initially, they all hold null which is a special object reference that means "no object."
Your subclass constructors each create a new object, and store its identity in C.
Objects have types which can be known at run-time, and variables have types which are only known at compile time. The compiler will reject your code if it can prove that you assign the identity of some object to an object reference variable with an incompatible type.
The type of variable C, Object, is compatible with any object. You can assign a String reference to it, or you can assign an Integer reference to it, or you can assign a ForkJoinPool.ForkJoinWorkerThreadFactory reference to it. The downside, is that the compiler will only let you perform operations through C that can be performed on every type of object. The compiler won't allow you to call C.toUpperCase()---not even if it holds a reference to an actual String object.---because you have explicitly declared that C may hold references to things that are not Strings.
I would not write the code that you wrote, but it's hard to say what I would write instead, because your example doesn't actually do anything. You'll get better answers in this forum if you ask questions about how to solve actual problems.
Understand, first and foremost, the difference between an object and a reference. An object is something that you create with new and it has a specific type (class) that is fixed from the moment it's created. A reference is a "pointer" to an object.
A single reference can point to different types of objects at different times, so long as the declared type of the reference is the class of the object or its superclass. Object is the superclass of all objects (including arrays, but not "scalars" like int, char, float, etc) and hence a reference declared with type Object can point to any object.
Of course, if you have a reference declared as Object then even if you (supposedly) know what's in it, the compiler and JVM haven't a clue. So you need to "cast" the reference to the appropriate type before you can use it. Eg, knowing that you previously stored a pointer to a String object into C, you might do:
String castFromC = (String)C;
Char charFromC = castFromC.charAt(5);
If it turns out that C is not a String but is instead an Integer, then the (String) "cast" operation will fail at runtime with a ClassCastException.
From my understanding of your concern, your posted code looks OK to me. However, it will force you to cast C to a specific type for it to be "operable" since you just declared the object reference C as just a "reference pointing to an Object type of Java object".
To avoid the ugly typecasting, you can generify your superclass like this
class SuperClass<T> {
String A;
String B;
T C;
public SuperClass(){}
}
And in your subclasses, you can use it like this
class SubClassA extends SuperClass<String> {
public SubClassA () {
C = new String();
}
}
class SubClassB extends SuperClass<Integer> {
public SubClassB () {
C = new Integer(0);
}
}
Also, it is a convention that your instance variables should have a meaningful name starting with lowercase, declared private, and exposed to other classes with public getters and setters.
Given the follow class hierarchy what are the dynamic and static types for the following statements?
Class hierarchy:
class Alpha {}
class Beta extends Alpha {}
class Gamma extends Alpha {}
class Epsilon extends Alpha{}
class Fruit extends Gamma{}
class Golf extends Beta {}
class Orange extends Fruit{}
For each of the following statements, Static Type? Dynamic Type?:
Fruit f = new Fruit();
Alpha a = f;
Beta b = f;
a = b;
Gamma g = f;
My answers/questions
I understand that Fruit f = new Fruit() will be of both static and dynamic type Fruit.
Alpha a = f; Will be of type Alpha at at compile time (static) and type Fruit at runtime (dynamic).
Gamma g = f; Will be of type Gamma at compile time (static) and type Fruit at runtime (dynamic).
However I do not know the other two answers. Beta b = f is an instance in which two subclasses of the same super class are assigned to one another so I'm not sure if it would be of type Beta or type Alpha at compile time (static). And a = b is an assignment after declaration so I'm not sure what the answer for that would be. Someone please help me out thanks!
I'm typing this in a hurry, so pls excuse any typos (I'll fix those later when I get a chance).
I understand that Fruit f = new Fruit() will be of both static and dynamic type Fruit.
I think you are confusing a bit the terms static and dynamic types with compile-time and run-time types (or as in C++ when you assign the address of a object of type A to a pointer of type B with B being the parent class of A.)
Barring reflection tricks, there is no dynamic typing in Java. Everything is statically typed at compile time. The type of an object at run-time is the same as the one it got compiled to.
What is happening is that you are confusing object references (a, b, c, f) with actual objects instantiated in the heap (anything created with new.)
In Java, f is an object reference, not the object itself. Moreover, the reference type of f is Fruit and sub-classes of it. The object (new Fruit()) that you assign to it happens to be of type Fruit.
Now all the other references in your sample code, a is of type reference to A and sub-classes of it; b is of type reference to B and sub-classes of it; etc, etc.
Keep this in mind because it is very important.
Alpha a = f; Will be of type Alpha at at compile time (static) and type Fruit at runtime (dynamic).
a is of type 'reference to type A and sub-classes'.
f is of type 'reference to type Fruit and sub-classes'.
The object f points to is of type 'Fruit'. When you say 'a = f' you are not assigning 'f' to 'a'. You are saying 'a now will reference that thing f is currently referencing to'.
So after that assignment, what is a referencing? The object of type Fruit the object reference f pointed to at the time of assignment.
Remember, a, b, g, f, they are not objects. They are references or handles to objects created one way or another with the new operator.
A reference variable such as a, b or f are different beasts from the objects created with new. But it just so happen that the former can point to the later.
The type of the object created with new at run-time is the same as the one determined at compile time.
Gamma g = f; Will be of type Gamma at compile time (static) and type Fruit at runtime (dynamic).
Same as above. The variable g is an object reference of type reference to type Gamma and sub-classes. In this assignment, g is made to point to the same object pointed by f. What is the type of that object? The same given at compile time: Fruit.
However I do not know the other two answers. Beta b = f is an instance
in which two subclasses of the same super class are assigned to one
another so I'm not sure if it would be of type Beta or type Alpha at
compile time (static).
b is of type reference to type Beta and sub-classes of it. The object it points to after the assignment b = f is of type Fruit, the type it had at compile time.
The type of object references a, b, g, and f is determined at compile time. They are statically typed and do not change at run-time.
The type of an object created with new is also determined at compile time. They are also statically typed and do not change at run-time.
The objects, the stuff object references a, b, g and f point to at run-time, that is determined by whether the statements are found valid by the compiler. The assignments can change, but that has nothing do with whether the object references or the object themselves are statically or dynamically typed.
If you want to see a clear distinction between dynamic and static typing consider the following:
// Java, statically typed.
int x = 3;
x = 5; // good
x = "hi"; // compiler error
## Ruby, dynamically typed
x = 3 # ok
x = 5 # ok
x = "hi" # still ok
Then there is the distinction between strongly typed and weakly/duck typed languages (both of which can be dynamically typed.) There is plenty of literature on this subject out there.
Hope it helps.
the dynamic and static types for the following statements
Erm, a statement doesn't have a type, at least there is no such notion in the Java Language Specification. The spec does define two different kinds of types: the declared type of variable, field, or parameter, and the runtime class of an object.
As the name indicates, the declared type of a variable, field or parameter is the type you mention in the declaration. For instance, the declaration Foo bar; declares a variable named bar of type Foo.
The runtime class of an object is determined by the class instance or array creation expression used to construct it, and remains the same throughout the lifetime of that object.
So the code:
Integer i = 1;
Number n = i;
Object o = n;
declares 3 variables of types Integer, Number and Object, respectively, all of which refer to a single object with runtime class Integer.
First to clarify "Reference Variable" type:
Object obj;
Points to nothing and the Reference Variable obj would have NO type.
Now
Object obj = new String();
System.out.println(obj.getClass());//prints class java.lang.String
obj points to a String and the Reference Variable obj has type String.
The point is Java is a statically typed language and all reference type variables have a type assigned at compile time. The reference variable obj can point to some other object as long as it is a subclass of Object. In this case
almost anything. Consider
Object obj = new String();
System.out.println(obj.getClass());//prints class java.lang.String
Number num = new Byte((byte)9);
obj = num;
System.out.println(obj.getClass());//prints class java.lang.Byte
At Runtime, same as compile time, the reference variable obj has type Byte.
The static/dynamic type of an object, for me, has to do with inheritance.
More specifically the overriding mechanism. Also known as Dynamic Polymorphism
and Late Binding.
Consider overriding the equals() in class Object:
public class Types {
#Override
public boolean equals(Object obj){
System.out.println("in class Types equals()");
return false;//Shut-up compiler!
}
public static void main(String[] args){
Object typ = new Types();
typ.equals("Hi");//can do this as String is a subclass of Object
}
}
Now we know that the type of the reference variable typ is Types.
Object typ = new Types();
When it comes to
typ.equals("Hi");
This is how I think the compiler thinks.
If the equals() is
1.NOT static and final, which it is.
2.Referenced from a base class (more on this soon).
then the compiler defers which method gets called to the JVM. The exact method that is invoked depends on the Dynamic Type(more soon) of the variable that calls the method. In our case the reference variable is typ.
This is known as Dynamic Method Invocation.
Now Referenced from a base class:
From the above code
Object typ = new Types();
typ.equals("Hi");
Type Object could be regarded as the base type of typ, also known as the Static Type of a reference variable and the equals() is referenced from the base type, in this case Object.
if we had
Types typ = new Types();
There would be no reference from a base type and hence no Dynamic Method Invocation.
Now to the Dynamic Type of a reference variable.
Object typ = new Types();
typ.equals("Hi");
The Dynamic Type of typ is Types and according to Dynamic Method Invocation, the equals() in class Types would be called at runtime.
Also lets say we had another class that extends Types, TypesSubClass.
And TypesSubClass also had a overridden equals(). Then
Object typ = new TypesSubClass();
typ.equals("Hi");
Would make the Dynamic Type of typ TypesSubClass and TypesSubClass's
equals() would be called at runtime.
To be honest, I personally didn't know why we needed all of this and have posted a question regarding this. check
What is the reason behind Dynamic Method Resolution in a staticlly typed language like Java
The concrete, runtime type of f is Fruit (as you correctly stated in your question).
So Beta b = f; initilalizes a variable whose declared, compile-time type is Beta, and whose runtime type is Fruit. This won't compile though, because the compile-time type of f is Fruit, and Fruit is not a subclass of Beta, so f can't be assigned to a variable of type Beta.
In a = b;, b, whose runtime type is Fruit (see above) is assigned to the variable a, declared as Alpha a. So a's compile-time type is Alpha and its runtime type is Fruit.
You should take a look at this article: http://www.sitepoint.com/typing-versus-dynamic-typing/
Static typing is when a language does not require the variable to be initialized.
eg.
/* C code */
static int num, sum; // explicit declaration
num = 5; // now use the variables
sum = 10;
sum = sum + num;
Dynamic typing is when a language requires the variable to be initialized.
eg.
/* Python code */
num = 10 // directly using the variable
While reading Horstmann's book, I've encountered this about subtype relationship in Java:
S is a subtype of T if:
S is an array type and T is Cloneable or Serializable
And I couldn't truly understand what this is supposed to mean. Can anyone help?
Thanks!
This is a simple example of subtyping.
The Array type implements the Cloneable and Serializable interfaces.
However, these interfaces do not specify any methods as part of the method contract. In fact, these are only marker interfaces whose existence gives details to the compiler and users of your classes without directly requiring any functionality.
For example:
Note that this interface does not contain the clone method. Therefore, it is not possible to clone an object merely by virtue of the fact that it implements this interface. Even if the clone method is invoked reflectively, there is no guarantee that it will succeed.
I think what is causing your confusion is that "cloneable" and "serializable" are attributes in the English language, and the phrase "is Cloneable" is commonly used to express that a class implements the Cloneable interface. However, here they only denote exactly the interface names Cloneable and Serializable, and "is" is to be understood as "is exactly/equals". That is, if T IS the interface Cloneable or if T IS the interface Serializable, then any array type is a subtype of T.
An array type is any type of the form U[], where U is an arbitrary type, i.e., non-void primitive or reference type (note that an array type, even of a primitive type, also is a reference type). This has nothing to with the class java.lang.reflect.Array, which is not an array type!
You cannot assign any array to a reference variable of a class implementing Cloneable or Serializable, but only to a reference variable of exactly the type Cloneable or Serializable. Hence, if you have
int[] a = ...; // primitive array
Object[] b = ...; // reference type array
int[][] c = ...; // reference type array, since int[] is a reference type
then the following is legal:
Cloneable x = a; x = b; x = c;
Serializable y = a; y = b; y = c;
class Person { void f() {} }
class Student extends Person { void f() {} }
So when I execute the following code:
Person p = new Student();
((Person) p).f();
Why is the f() function in the Student class called when the variable is cast as a Person? I understand why the f() function is called when it is just p.f(), but I guess I'm just confused to what exactly the cast does.
This is one of the cornerstones of Object Oriented: Polymorphism. You have all kinds of Person entities each doing f() in its own way! It is the actual instance of the object and not what you cast it to that does f().
You can always convert subtypes to Type this casting is always allowed in java however because instance is of subtype subtype methods will get executed.
You can check Here for more information
In Java there are two types of reference variable casting:
Downcasting: If you have a reference variable that refers to a subtype object, you can assign it to a reference variable of the
subtype. You must make an explicit cast to do this, and the result is
that you can access the subtype's members with this new reference
variable.
Upcasting: You can assign a reference variable to a supertype reference variable explicitly or implicitly. This is an inherently
safe operation because the assignment restricts the access
capabilities of the new variable.
Yes, you need to implement the interface directly or indirectly to enable assigning your class object reference to the interface type.
When invoking methods, it will be always executed on object type, not reference type due to ploymorphsim.
The same method -- the one in Student -- will be called whether or not you've cast the Student to a Person.
Up-casting a reference (from subclass to superclass) serves no real function (and in fact will generally be a no-op internally). Down-casting, on the other hand, tells the JVM that you believe the superclass to be of the specified subclass, and if it's not a cast error will be raised.
(I'll give gefei credit for pointing out an omission above: The cast ALSO tells the compiler the type, and the compiler uses this information to know what methods and fields the object (supposedly) has. This is not strictly necessary to compile a Java program to bytecodes, but it does allow the compiler to do the usual compile time validity checks.)
The upcasting is irrelevant here. The overridden method will always be called if present.
The cast in your example is totally useless. The compiler knows that p is of type Person. More interesting would be a narrowing cast:
Person p = getPerson();
if (p instanceof Student) {
Student s = (Student) p;
// do something Student-specific with s
}
Casting reference types in Java does not change the nature of the object; it only tells the compiler what assumptions can be made about the object. At run time, the object's actual type is used to determine what method is called. (Note that this is not true of primitive types; casting an int to a byte will change the data.)
I have something unclear concerning casting reference variable in Java.
I have two classes A and B. A is the super class of B.
If I have the two objects, and then the print statement:
A a = new A(); //superclass
B b = new B(); //subclass
System.out.println ((A)b);
then what exactly is happening when the println method is executed?
I know that because B is a subclass of A, I am allowed to make the following cast:
A a2 = (A)b;
I also know that when println takes a reference variable as argument, then the toString() method of the class, which has created the object-argument, is invoked (implicitly). This is so, because the method println() is looking for an argument of type String, and the toString() method represent the object as a string. And even if we don't write toString(), the method is invoked - implicitly. So, the following two statements are equivalent:
System.out.println (b);
System.out.println (b.toString());
So, my question is: what is the implicit action taken when we have
System.out.println ((A)b);
?
I suppose that the type of the reference variable b is automatically changed from B to A. The variable should still be pointing to the same object - the one created with
B b = new B();
but just the type of b would be now changed. Is this correct?
Another question: even though I have changed the type of b to the type of the superclass, are the overriden methods in the subclass going to be called, and not those of the superclass?
Thanks a lot.
Regards
The cast has no impact in this case.
The System.out.println(XXX) takes parameters of different types (multiple overloaded versions) but in this case you would get the version that takes Object. Since every object in Java supports toString(), toString is invoked on the actual argument, no matter what it is.
Now, since all methods in Java are dispatched dynamically, the version that runs is the version that corresponds to the dynamic type. Casting an object of B to A only changes the static (declared) type of the expression. The dynamic type (what's really in there) is still a B. Therefore, the version in B gets invoked.
There are many declarations of println(...) in the PrintStream class (which is the type of System.out).
Two of them are:
void println(String x)
void println(Object x)
When you call println((A)b) the compiler chooses to call println(Object) because A is not String (or any of the other types that println supports). When you call println(b.toString()), the compiler chooses println(String) because you are passing a String.
In your case, casting b to A has no effect since println() doesn't have a declaration for either A or B types. But the cast will still occur (because you asked for it), or maybe it won't because the compiler optimises it away as it knows it is redundant and it can't fail and has no effect.
It is not idiomatic to write:
A a2 = (A)b;
as this is redundant since B is a subclass of A. It may be that the compiler will optimise away the cast (which is a run-time operation to check whether an object is of a particular type, never to change it's type).
Once an object of type B is constructed, it's type never changes. It is always a B:
class B extends/implements A {...}
B b = new B(); // construct a B
A a = b; // assign a B to an A variable, it's superclass
A a = (A) b // as above including check to see that b is an A (redundant, may be optimised away).
B b = a; // Syntax error, won't compile
B b = (B) a // Will check whether a is of type B then assign to variable b
In the last case, since B is a subclass of A, it may be that a holds an instance of B and the cast will succeed. Or it may be that a holds an instance of some other class that extends/implements/is A and isn't a B and you'll get a ClassCastException.
So since an object of type B always retains it's identity (it's "B"-ness) then any (instance-) methods called on that object will always call B's implementation regardless of whether the variable through which you access the object was declared as A or B.
Remember, you can only call methods that are declared in the class that the variable is defined as.
So for example, if B declares a method b_only() then the compiler won't allow you to write a.b_only(); you could write ((B)a).b_only() though.
Since Java methods all have dynamic dispatch, which function gets called doesn't depend on the static type of the reference. Therefore, the results will be the same with or without the cast. [The results could be different if you were downcasting - the casting version could throw an exception]
Is this correct?
Sort of. The result of the casting expression would be of the A type. The type of the 'b' variable will always remain of type B.
Another question: even though I have changed the type of b to the type of the superclass, are the overriden methods in the subclass going to be called, and not those of the superclass?
The instance methods of the underlying object will be called. Example:
class Foo {
public static void main(String[] args) {
B b = new B();
assert "B".equals(((A) b).m());
}
}
class A {
String m() { return "A"; }
}
class B extends A {
String m() { return "B"; }
}
Always think of your object as the type it's instantiated as (B in your case). If it's upcast to A think of it as--hmm--think of it as B putting on A clothes. It may look like an A, and you may not be able to do any of the nice B things you want to do, but inside the clothes it's still a B--the clothes don't change the underlying object at all.
So the summary would be--you can only call the methods in A, but when you call it, it goes straight through and executes it as it would if it was a B.
I think when we use reference variable in java and by using this variable we can assign a object of any class type. most of the cases we create a reference variable of Interface and abstract class because we can't create the object of interface and abstract class so assign the object of class in reference variable of Interface or abstract class.
Ex-
Interface X {
public abstract void xx();
public abstract void yy();
}
Class XXX implements X {
...........
}
Class XY extends XXX {
X xy = new XXX();
}
here xy is a reference of Interface X and assign the object of Class XXX in the reference of Interface.
so according to my point of view by using reference variable we can also use interface to participate in Object creation.
The casting, as has been mentioned, is irrelevant in this case due to overridden methods being dynamically bound. Since the toString is present in all objects it meets this condition and thus the object type and method to call are determined at runtime.
Please note though, this is NOT the case with all methods since only overridden methods are dynamically bound. Overloaded methods are statically bound. Many of the answers here mention that java methods are always dynamically bound, which is incorrect.
See this question for a more detailed explanation.
question: even though I have changed the type of b to the type of the superclass, are the overriden methods in the subclass going to be called, and not those of the superclass?
in this case the method of subclass b is called ; to convincingly understand why; you may relate to the following real world scenario
consider a parent class Father exhibiting a behaviour(method): height
defined as
the father is tall ;height = 6'2"
Son is a child class inheriting the height behavior from Father ;as a result he is also tall; height being 6' clearly overriding the behaviour
whenever your subclass Son calls the behavior height on his name he displays the overridden behavior i.e his own height 6' .