Confused about late binding/casting in Java - java

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.)

Related

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.

Polymorphism in Java - can I set an object to a specific type?

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.

Does casting change the declared/reference type at run-time?

First, let me be clear about what I mean by the declared type.
Assume SuperBoss is a superclass of the class Boss.
SuperBoss mrBond = new Boss();
SuperBoss is the declared type, and Boss is the actual type.
Personally, I think the declared type is changed at run-time due to the following run-time exception:
SuperBoss mrWayne = new SuperBoss();
((Boss)mrWayne).randomMethod();
//Exception: java.lang.ClassCastException: SuperBoss cannot be cast to Boss
I know this may seem trivial, but I'm going to be tutoring next quarter, and I don't want to teach the students the wrong thing. And my professor and her assistant this quarter did not agree with each other on this subject. My professor believes that casting does indeed completely change the declared type at run-time for a single statement. The T.A. strongly believed that at run-time, the cast is merely checked, but doesn't actually change the declared type.
My professor believes that casting does indeed completely change the declared type at run-time for a single statement. The T.A. strongly believed that at run-time, the cast is merely checked, but doesn't actually change the declared type.
In fact, I think that they are both right in a sense. There is no contradiction in what they are saying ... if you can figure out what they are actually saying.
The declared type of mrWayne does not change. The declared type of ((Boss) mrWayne) does "change". Or at least, it is different to the declared type of mrWayne.
The real problem here is that someone is using sloppy terminology ... and people are talking past each other.
OK consider this example:
public class Test {
public static void method(Object t) {
system.out.println("Its an object");
}
public static void method(Test t) {
system.out.println("Its a test");
}
public static void main(String[] args) {
Test t = new Test();
method(t);
method((Object) t);
}
}
This should output:
Its a test
Its an object
Why? Because the declared type of (Object) t is Object ... not Test. And it is the declared type (not the runtime type) that determines which of the two overloads of method is used for a particular call.
See?
It all depends on what you are talking about. The declared type of the variable, or the declared type of the expression.
The "declared" type is what you declared to the compiler. It does not change after the program is compiled.
The "runtime" type is the type of the actual object assigned to a variable. It only changes when you assign a new object. (It never changes for a given object, no object instance can change its class).
The cast bridges the two: It checks the runtime type and then allows you to declare that type. If the check failed, the program will abort (with a RuntimeException). You need to do this when you have more type information then the compiler. You can then "declare" to the compiler that the object in question is indeed a "Boss", and not just a "SuperBoss" (which is the best the compiler could otherwise guarantee).
My professor believes that casting does indeed completely change the declared type at run-time for a single statement.
Casting "declares" a more specific type at compile-time. But it also includes a runtime check to make this safe.
The T.A. strongly believed that at run-time, the cast is merely checked, but doesn't actually change the declared type.
The check happens at runtime, but having the cast in your code allows you are more specific type declaration at compile-time.
((Boss)mrWayne).randomMethod();
Two things happen:
compile time: You declare that this is a Boss. Otherwise you couldn't call the method.
run time: The JVM checks if that object really is a Boss.

Java Syntax confusion, why put () around a class?

I'm studying Prototype Design pattern I can't understand the syntax below. Can you explain it to me? What does it mean for a class to be put in parentheses and initialized like this :
Person person2 = (Person) person1.doSomthing();
Code in context:
// code in int main
Person person1 = new Person("Fred");// this is understood
System.out.println("person 1:" + person1);// this is understood
Person person2 = (Person) person1.doClone();//not understood
System.out.println("person 2:" + person2);// this is understood
Is this syntax in java for casting?
It is a cast. In other words, the doSomething() method is most likely not declared to return a Person. So you need to first cast the returned value to a Person before assigning it to person2.
If doSomething does return a Person, then the cast is not necessary.
And if the actual type of the object returned by doSomething is not assignable to a Person, the cast will throw a ClassCastException at runtime.
More info about it in the JLS #15.16:
A cast expression converts, at run-time, a value of one numeric type to a similar value of another numeric type; or confirms, at compile-time, that the type of an expression is boolean; or checks, at run-time, that a reference value refers to an object whose class is compatible with a specified reference type.
The parentheses and the type they contain are sometimes called the cast operator.
It's called type casting. What you are saying is that you want to ensure that the JVM makes an attempt to force whatever type of object is returned by that call to be a Person.
Another thing not mentioned by assylias is that this has two additional features:
If the actual returned object is a subclass of Person, it will change the returned value into a Person. This can be useful for when you want to make sure that whatever subclass is actually used, it'll hopefully behave just like a Person.
If Person is an interface, it will let you step outside of the inheritance hierarchy of that particular implementation altogether and use someone else's Person implementation. This is great for use with APIs like Spring which let you configure after build time how classes are chained together in things like security configurations and such.
It is simply used to cast it (person1.doSomthing()) into Person type. Because it may not be returning a Person type, but something that can be casted into a Person type.
You're casting the result of Person.doClone() to also be of type Person. Presumably Person.doClone() returns an Object or similar.
Person person2 = (Person) person1.doSomthing();
It Type casting to Person type which Object returns from person1.doSomthing().
Person person2 = (Person) person1.doClone();
Here it clone the person1 Object cast it into person object.
This is a typecast, to ensure that the object type that is returned from calling person1.doClone() is of type Person, so that it can be assigned to your person2 variable.
If the doClone() method is defined in the Person class, I would expect it to return an object of type Person. In this scenario, the cast is benign and unnecessary.
However, doClone() may be defined in a superclass of Person, and returning a more generic type, thus a narrowing cast may be desired.
If doClone does not return an instance of type Person (or some other class in Person's inheritance hierarchy), then a java.lang.ClassCastException will be thrown.

Casting reference variable in Java

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' .

Categories

Resources