I know the three following methods that can be used for casting objects.
Object o = "str";
String str1 = (String) o; // Method 1
String str2 = o.toString(); // Method 2
String str3 = String.class.cast(o); // Method 3
Which approach is better, and what are the pros/cons of one method as compared to the others?
What happens to the object during the time of casting internally?
The second method that you show is not casting; it's simply calling the toString() method on an object, which is not different than any other method call:
String str2 = o.toString();
The effect of the first and third methods is essentially the same. I would prefer using the first method.
What happened with object on the time of casting internally?
Nothing happens to the object. Casting is not a way to somehow automatically convert objects from one type to another type. The only thing that a cast does, is tell the compiler to accept the assignment statement and not check the types for this statement. You're saying to the compiler "I know better than you what kind of object this is, so let me do this assignment and don't complain about the type".
In your example, the type of the variable o is Object. When you assign o to a variable of type String, the compiler won't allow it because it checks the types, and it can't be sure that o in fact refers to a String object. So you use a cast to tell the compiler "I know this is a String object, so let me do this assignment".
The type will still be checked, but at runtime, not at compile time. If, at runtime, the type of the object is not String, you'll get a ClassCastException.
The first one casts the o reference, whose declared type is Object, and whose actual, concrete type is String, to a String. This is the canonical way of casting. That will only work if the object referenced by o is actually of type String. If it isn't, you'll have a ClassCastException.
The second one doesn't cast at all. It calls the toString() on the object referenced by o. That will always work, but it's really really different from a cast.
The third one uses reflection to do a cast. That will have the same effect as the first one, and will work in the same circumstances. But this will generally only be used when the code doesn't actually know the type of the class to cast to:
Class<?> someClassToCastTo = ...; // could be String.class or anything else, you don't know)
String str3 = someClassToCastTo.cast(o);
Nothing happens to the object when it's cast. Casting checks that the object is indeed of class String, and fails otherwise, that's all. But once cast to a variable of type String, you can access the methods that exist in String, and that you couldn't have called when you had a variable of type Object.
Here it goes:
Object o = "str";
String str1 = (String) o;
This works only when the object actually is a string.
String str2 = o.toString();
When you use toString() on a String object, you obtain the string itself. It will throw an exception when object o is null.
String str3 = String.class.cast(o);
Mainly used when using reflection, i.e. when you want to retrieve the Class token via reflection.
You can actually reason this out yourself.
$ cat Cast.java
public class Cast {
private Cast() {}
public static void main(String[] args) {
Object o = "str";
String str1 = (String) o; // Method 1
String str2 = o.toString(); // Method 2
String str3 = String.class.cast(o); // Method 3
}
}
$ javac Cast.java
$ javap -c Cast
Compiled from "Cast.java"
public class Cast {
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String str
2: astore_1
3: aload_1
4: checkcast #3 // class java/lang/String
7: astore_2
8: aload_1
9: invokevirtual #4 // Method java/lang/Object.toString:()Ljava/lang/String;
12: astore_3
13: ldc #3 // class java/lang/String
15: aload_1
16: invokevirtual #5 // Method java/lang/Class.cast:(Ljava/lang/Object;)Ljava/lang/Object;
19: checkcast #3 // class java/lang/String
22: astore 4
24: return
}
As you can see,
Method 1 is just a checkcast opcode.
Method 2 is an invokevirtual opcode.
Method 3 is an ldc (load class), followed by invokevirtual and checkcast.
Obviously, Method 3 is inferior in terms of verbosity, readability, and performance.
Of 1 and 2, which is better?
checkcast means "look at this object: is it really a String?" — if so, proceed with the assignment; if not, throw a ClassCastException.
invokevirtual means "look up which toString() method to call based on the class of o" — in this case, it's String.toString(). The obvious implementation of that method is
public String toString() {
return this;
}
Given the choice between asking "Is it a String? and "What type is it? which method do we call as a result? Let's execute String.toString() on with str2 as the implied parameter." — Method 1 should be quite a bit simpler.
Java is strongly typed language and it only allows casting an object to one of it parent classes or interfaces. That is, if you have the following:
class A {}
interface I {}
class B extends A implements I {}
class C {}
You can cast an object of type B like so:
B b = new B();
A a = b; // no need for explicit casting
I i = b;
Object o = b; // Object is implicit parent of A
C c = b; // ERROR C isn't a parent class of b
This is called upcasting. You can also downcast:
A a = new B();
B b = (B) b;
you need to use explicit cast here and JVM will check in runtime if the cast is really allowed.
The String.class.cast(o) casting is useful, when you don't know the specific type you're casting to in compile time.
The .toString() isn't casting. It's just a method, which is supposed to return a String representation of your object. But the representation isn't the object, it's just a representation. This method is defined in the Object class, so it's present in all classes and is baked in the language somewhat. That is, if you write
String s = "Hell " + o;
JVM will call o.toString() method for you to obtain it representation.
Related
In this question, user #Holger provided an answer that shows an uncommon usage of anonymous classes, which I wasn't aware of.
That answer uses streams, but this question is not about streams, since this anonymous type construction can be used in other contexts, i.e.:
String s = "Digging into Java's intricacies";
Optional.of(new Object() { String field = s; })
.map(anonymous -> anonymous.field) // anonymous implied type
.ifPresent(System.out::println);
To my surprise, this compiles and prints the expected output.
Note: I'm well aware that, since ancient times, it is possible to construct an anonymous inner class and use its members as follows:
int result = new Object() { int incr(int i) {return i + 1; } }.incr(3);
System.out.println(result); // 4
However, this is not what I'm asking here. My case is different, because the anonymous type is propagated through the Optional method chain.
Now, I can imagine a very useful usage for this feature... Many times, I've needed to issue some map operation over a Stream pipeline while also preserving the original element, i.e. suppose I have a list of people:
public class Person {
Long id;
String name, lastName;
// getters, setters, hashCode, equals...
}
List<Person> people = ...;
And that I need to store a JSON representation of my Person instances in some repository, for which I need the JSON string for every Person instance, as well as each Person id:
public static String toJson(Object obj) {
String json = ...; // serialize obj with some JSON lib
return json;
}
people.stream()
.map(person -> toJson(person))
.forEach(json -> repository.add(ID, json)); // where's the ID?
In this example, I have lost the Person.id field, since I've transformed every person to its corresponding json string.
To circumvent this, I've seen many people use some sort of Holder class, or Pair, or even Tuple, or just AbstractMap.SimpleEntry:
people.stream()
.map(p -> new Pair<Long, String>(p.getId(), toJson(p)))
.forEach(pair -> repository.add(pair.getLeft(), pair.getRight()));
While this is good enough for this simple example, it still requires the existence of a generic Pair class. And if we need to propagate 3 values through the stream, I think we could use a Tuple3 class, etc. Using an array is also an option, however it's not type safe, unless all the values are of the same type.
So, using an implied anonymous type, the same code above could be rewritten as follows:
people.stream()
.map(p -> new Object() { Long id = p.getId(); String json = toJson(p); })
.forEach(it -> repository.add(it.id, it.json));
It is magic! Now we can have as many fields as desired, while also preserving type safety.
While testing this, I wasn't able to use the implied type in separate lines of code. If I modify my original code as follows:
String s = "Digging into Java's intricacies";
Optional<Object> optional = Optional.of(new Object() { String field = s; });
optional.map(anonymous -> anonymous.field)
.ifPresent(System.out::println);
I get a compilation error:
Error: java: cannot find symbol
symbol: variable field
location: variable anonymous of type java.lang.Object
And this is to be expected, because there's no member named field in the Object class.
So I would like to know:
Is this documented somewhere or is there something about this in the JLS?
What limitations does this have, if any?
Is it actually safe to write code like this?
Is there a shorthand syntax for this, or is this the best we can do?
This kind of usage has not been mentioned in the JLS, but, of course, the specification doesn’t work by enumerating all possibilities, the programming language offers. Instead, you have to apply the formal rules regarding types and they make no exceptions for anonymous types, in other words, the specification doesn’t say at any point, that the type of an expression has to fall back to the named super type in the case of anonymous classes.
Granted, I could have overlooked such a statement in the depths of the specification, but to me, it always looked natural that the only restriction regarding anonymous types stems from their anonymous nature, i.e. every language construct requiring referring to the type by name, can’t work with the type directly, so you have to pick a supertype.
So if the type of the expression new Object() { String field; } is the anonymous type containing the field “field”, not only the access new Object() { String field; }.field will work, but also Collections.singletonList(new Object() { String field; }).get(0).field, unless an explicit rule forbids it and consistently, the same applies to lambda expressions.
Starting with Java 10, you can use var to declare local variables whose type is inferred from the initializer. That way, you can now declare arbitrary local variables, not only lambda parameters, having the type of an anonymous class. E.g., the following works
var obj = new Object() { int i = 42; String s = "blah"; };
obj.i += 10;
System.out.println(obj.s);
Likewise, we can make the example of your question work:
var optional = Optional.of(new Object() { String field = s; });
optional.map(anonymous -> anonymous.field).ifPresent(System.out::println);
In this case, we can refer to the specification showing a similar example indicating that this is not an oversight but intended behavior:
var d = new Object() {}; // d has the type of the anonymous class
and another one hinting at the general possibility that a variable may have a non-denotable type:
var e = (CharSequence & Comparable<String>) "x";
// e has type CharSequence & Comparable<String>
That said, I have to warn about overusing the feature. Besides the readability concerns (you called it yourself an “uncommon usage”), each place where you use it, you are creating a distinct new class (compare to the “double brace initialization”). It’s not like an actual tuple type or unnamed type of other programming languages that would treat all occurrences of the same set of members equally.
Also, instances created like new Object() { String field = s; } consume twice as much memory as needed, as it will not only contain the declared fields, but also the captured values used to initialize the fields. In the new Object() { Long id = p.getId(); String json = toJson(p); } example, you pay for the storage of three references instead of two, as p has been captured. In a non-static context, anonymous inner class also always capture the surrounding this.
Absolutely not an answer, but more of 0.02$.
That is possible because lambdas give you a variable that is inferred by the compiler; it is inferred from the context. That is why it is only possible for types that are inferred, not for types we can declare.
The compiler can deduce the type as being anonymous, it's just that it can't express it so that we could use it by name. So the information is there, but due to the language restrictions we can't get to it.
It's like saying :
Stream<TypeICanUseButTypeICantName> // Stream<YouKnowWho>?
It does not work in your last example because you have obviously told the compiler the type to be : Optional<Object> optional thus breaking anonymous type inference.
These anonymous types are now (java-10 wise) available in a much simpler way too:
var x = new Object() {
int y;
int z;
};
int test = x.y;
Since var x is inferred by the compiler, int test = x.y; will work also
Is this documented somewhere or is there something about this in the JLS?
I think it is not a special case in anonymous class that need to introduced into JLS . as you have mentioned in your question you can access anonymous class members directly, e.g: incr(3).
First, let's look at a local class example instead, this will represent why chain with the anonymous class could access its members. for example:
#Test
void localClass() throws Throwable {
class Foo {
private String foo = "bar";
}
Foo it = new Foo();
assertThat(it.foo, equalTo("bar"));
}
As we can see, a local class members can be accessed out of its scope even if its members is private.
As #Holger has mentioned above in his answer, the compiler will create an inner class like as EnclosingClass${digit} for each anonymous class. so Object{...} has it own type that derived from Object. due to the chain methods return it own type EnclosingClass${digit} rather than the type which is derived from Object. this is why you chain the anonymous class instance could works fine.
#Test
void chainingAnonymousClassInstance() throws Throwable {
String foo = chain(new Object() { String foo = "bar"; }).foo;
assertThat(foo,equalTo("bar"));
}
private <T> T chain(T instance) {
return instance;
}
Due to we can't reference anonymous class directly, so when we break the chain methods into two lines we actually reference the type Object which is derived from.
AND the rest question #Holger has answered.
Edit
we can conclude that this construction is possible as long as the anonymous type is represented by a generic type variable?
I'm sorry I can't find the JLS reference again since my English is bad. but I can tell you that it does. you can using javap command to see the details. for example:
public class Main {
void test() {
int count = chain(new Object() { int count = 1; }).count;
}
<T> T chain(T it) {
return it;
}
}
and you can see that checkcast instruction has invoked below:
void test();
descriptor: ()V
0: aload_0
1: new #2 // class Main$1
4: dup
5: aload_0
6: invokespecial #3 // Method Main$1."<init>":(LMain;)V
9: invokevirtual #4 // Method chain:(Ljava/lang/Object;)Ljava/lang/Object;
12: checkcast #2 // class Main$1
15: getfield #5 // Field Main$1.count:I
18: istore_1
19: return
I've come across a weird thing while learning Java. Consider the following program:
public class GetClassNameInheritance {
public static void main(String[] args) {
Employee e = new Employee();
Person p = (Person) e;
System.out.println(p.getClass());
}
}
class Person {
}
class Employee extends Person {
}
I was expecting the output to be Person because of the cast, but it is Employee! Honestly, I'm stumped and can't find an explanation. The official Oracle tutorial doesn't mention this behavior, and the docs seem too terse to be helpful. I can make out from other StackOverflow examples that this is something to do with "runtime class" but I don't understand the underlying idea. Can someone explain what's going on here?
In Java, Type casting does not change the type of object. Once an Employee, your object is always an Employee. Type casts do not work like in other languages such as C/C++.
Though the p reference is of type Person, it is in fact referring to an object of type Employee in the JVM heap. When you invoke the p.getClass() method, the reference in turn invokes the method on the object and not on the reference. That's the reason you see Employee in your output.
The only type of casts that would work is the one that you can perform on primitives.
int x = (int)2.5f;
The return value will be type cast to int.
You can read more about type casts and conversions here:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html
http://www.wideskills.com/java-tutorial/java-object-typecasting
Well let's look what happens when the main method is compiled:
0: new #2 // class Employee
3: dup
4: invokespecial #3 // Method Employee (Constructor)
7: astore_1 // Save local variable "e"
8: aload_1 // Load local variable "e"
9: astore_2 // Save to local variable "p"
10: getstatic #4 // Field System.out PrintStream;
13: aload_2 // Load local variable "p"
14: invokevirtual #5 // Method Object.getClass()
17: invokevirtual #6 // Method PrintStream.println(Object)
And if we were to look at a decompiler's interpretation of this bytecode:
public static void main(String[] args) {
Employee e = new Employee();
System.out.println(e.getClass());
}
The compiler has "optimized" the code which results in your cast being removed since it was deemed unnecessary. Anacron's answer explains why the compiler deemed it unnecessary.
If you want to know the super class of current class, you can use
p.getClass().getSuperclass() //returns person.
From the docs
getClass() always returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.
And a runtime class is one which has been instantiated with the constructor call.
Actually, if you will see, here Person p = (Person) e; the cast is not needed, you can always assign a derived class object to a base class.
For Example
List<String> list = new ArrayList<>();
Above will not throw an error.
I have spend whole day on this problem. My problem is how to make an MethodHandle.invokeExact invocation on an instance, whose class type is dynamically loaded at program runtime. To make problem more clear, i show my sample code below:
Class<?> expClass = new MyClassLoader().load(....)
//expClass is AddSample.class which is subclass of BaseTemplate
BaseTemplate obj = expClass.getConstructor(...)
.newInstance(...);
MethodHandle myMH = MethodHandles.lookup().findVirtual(expClass, methodName,..);
System.out.println("Object type "+obj.getClass()); //Print AddSample
// If obj is declared as "AddSample obj", the runtime would be OK.
assertEquals((int)myMH.invokeExact(obj,"addintaasdsa" , 10 , 20.0f), 12);
In this sample, expClass is dynamically loaded and its class type is AddSample. The obj instance in the next line is declared to be BaseTemplate, and its real type is AddSample. Class AddSample is subclass of BaseTemplate. Then a MethodHandle myMh is created to the add function of AddSample but the invocation of myMH fails because of receiverType does not match.
The myMH.invokeExact raises runtime error
java.lang.invoke.WrongMethodTypeException: expected (AddSample,String,int,float)int but found (Object,String,int,float)int
because the receiver of this myMH is declared to be on expClass (AddSample), but the receiver obj current provided is declared BaseTemaplte, though the obj's Class is AddSample. InvokeExact requires exact parameter match.
My problem might be simplified as:
how to cast an instance from its base type to a child type which is dynamically loaded?
BaseTemplate obj = ...
Class<?> newType = Class('AddSample') //dynamic loaded...
change obj's declared type to AddSample which is dynamically loaded..?
UPDATE:
Class<T> expClass = (Class<T>) new MyClassLoader().run(className, methodName, b);
BaseTemplate obj = ..
Class<T> newType = (Class<T>) obj.getClass().getClassLoader().loadClass("AddSample");
T tObj = newType.cast(obj);
assertEquals((int)myMH.invokeExact(tObj,"addintaasdsa" , 10 , 20.0f), 12);
Using cast does not help fix the problem which is the same previous result. The reason is still that the given parameter does not exact match myMH declaration. It would be more clear when i check the generated bytecodes:
L23 # For cast
LINENUMBER 126 L23
ALOAD 10: newType
ALOAD 8: obj
INVOKEVIRTUAL Class.cast (Object) : Object #tObj is Object and its real type is AddSample here
ASTORE 11
L24
LINENUMBER 128 L24
ALOAD 9: myMH # Push myMH to stack
ALOAD 11: tObj # Push tObj to Stack. tObj is declared Object type and its real type is AddSample.
LDC "addintaasdsa" #Push String to Stack
BIPUSH 10 #Push int to Stacl
LDC 20.0 #Push float to Stack
INVOKEVIRTUAL MethodHandle.invokeExact (Object, String, int, float) : int
the myMH points to
(AddSample,String,int,float)int, but given parameters:
(Object, String, int, float),
and this result in runtime error which I was shown previously.
Thanks
You can’t use invokeExact if the compile-time type of an argument doesn’t match the MethodHandle’s parameter type. It doesn’t help to play around with the Generic constructs like invoking cast on a Class<T>, the dynamic type is still unknown to the compiler.
Or, in other words, due to type erasure, the type of tObj is still Object on the byte code level. And this is the “invoked type” of the MethodHandle.
The simplest solution is to use invoke rather than invokeExact.
The only thing you can do if you want to use invokeExact, is to transform the MethodHandle to the type which you will eventually invoke, i.e. change the type of the first parameter to Object:
myMH=myMH.asType(myMH.type().changeParameterType(0, Object.class));
// now it doesn’t matter that obj has compile-time type Object
assertEquals((int)myMH.invokeExact(obj, "addintaasdsa", 10, 20.0f), 12);
To make sense, you'll have to type the method ( otherwise the cast is pointless):
public <T> void doSomething() {
BaseTemplate obj = ...
Class<T> newType = Class('AddSample');
T t = newType.cast(obj);
Without the method typing, you can't tie the dynamic class to the type to be cast to.
This question already has answers here:
Casting to generic type in Java doesn't raise ClassCastException?
(5 answers)
Closed 8 years ago.
I have some code which looks something like this (part of a negative test of the method get):
import java.util.*;
public class Test {
Map<String, Object> map = new HashMap<>();
public static void main (String ... args) {
Test test = new Test();
test.put("test", "value"); // Store a String
System.out.println("Test: " + test.get("test", Double.class)); // Retrieve it as a Double
}
public <T> T get(String key, Class<T> clazz) {
return (T) map.get(key);
}
public void put(String key, Object value) {
map.put(key, value);
}
}
I was expecting it to throw a ClassCastException but it runs through successfully printing:
Test: value
Why doesn't it throw?
That's because you are casting to the generic type T, which is erased at runtime, like all Java generics. So what actually happens at runtime, is that you are casting to Object and not to Double.
Note that, for example, if T was defined as <T extends Number>, you would be casting to Number (but still not to Double).
If you want to do some runtime type checking, you need to use the actual parameter clazz (which is available at runtime), and not the generic type T (which is erased). For instance, you could do something like:
public <T> T get(String key, Class<T> clazz) {
return clazz.cast(map.get(key));
}
I found a difference in the byte code when a method is called on the returned "Double" and when no method is called.
For example, if you were to call doubleValue() (or even getClass()) on the returned "Double", then the ClassCastException occurs. Using javap -c Test, I get the following bytecode:
34: ldc #15 // class java/lang/Double
36: invokevirtual #16 // Method get (Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
39: checkcast #15 // class java/lang/Double
42: invokevirtual #17 // Method java/lang/Double.doubleValue:()D
45: invokevirtual #18 // Method java/lang/StringBuilder.append:(D)Ljava/lang/StringBuilder;
The checkcast operation must be throwing the ClassCastException. Also, in the implicit StringBuilder, append(double) would have been called.
Without a call to doubleValue() (or getClass()):
34: ldc #15 // class java/lang/Double
36: invokevirtual #16 // Method get:(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
39: invokevirtual #17 // Method java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;
There is no checkcast operation, and append(Object) is called on the implicit StringBuilder, because after type erasure, T is just Object anyway.
You do not get a ClassCastException because the context in which you are returning the value from the map does not require the compiler to perform a check at this point as it is equivalent to assigning the value to a variable of type Object (See rgettman's answer).
The call to:
test.get("test", Double.class);
is part of a string concatenation operation using the + operator. The object returned from your map is just being treat as if it is an Object. In order to display the returned 'object' as a String a call to the toString() method is required and since this is a method on Object no cast is required.
If you take the call to test.get("test", Double.class); outside of the context of the string concatenation you will see that it does't work i.e.
This does not compile:
// can't assign a Double to a variable of type String...
String val = test.get("test", Double.class);
But this does:
String val = test.get("test", Double.class).toString();
To put it another way, your code:
System.out.println("Test: " + test.get("test", Double.class));
is equivalent to:
Object obj = test.get("test", Double.class);
System.out.println("Test: " + obj);
or:
Object obj = test.get("test", Double.class);
String value = obj.toString();
System.out.println("Test: " + value);
It is instructive to consider what the class looks like after removing type parameters (type erasure):
public class Test {
Map map = new HashMap();
public static void main (String ... args) {
Test test = new Test();
test.put("test", "value");
System.out.println("Test: " + test.get("test", Double.class));
}
public Object get(String key, Class clazz) {
return map.get(key);
}
public void put(String key, Object value) {
map.put(key, value);
}
}
This compiles and produces the same result that you see.
The tricky part is this line:
System.out.println("Test: " + test.get("test", Double.class));
If you had done this:
Double foo = test.get("test", Double.class);
then after type erasure the compiler would have inserted a cast (because after type erasure test.get() returns Object):
Double foo = (Double)test.get("test", Double.class);
So analogously, the compiler could have inserted a cast in the above line too, like this:
System.out.println("Test: " + (Double)test.get("test", Double.class));
However, it doesn't insert a cast, because the cast is not necessary for it to compile and behave correctly, since string concatenation (+) works on all objects the same way; it only needs to know the type is Object, not a specific subclass. Therefore, the compiler can omit an unnecessary cast and it does in this case.
It seems that Java is unable to process the cast with an inferred type, however if you use the Class.cast method, the call to get throws an exception as expected :
public <T> T get(String key, Class<T> clazz) {
return clazz.cast(map.get(key)) ;
}
Unfortunately, I am not able to explain that more thoroughly.
Edit : you might be interested by this Oracle doc.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How does the Java cast operator work?
Java casting implementation
I am always wondering how object casting works in Java. I understand for primitive type it will be more like in binary representation level, but what about Object? Is it kind of like Polymorphism or dynamic binding in that everything will be determined at runtime? For example:
class Parent{
void A(){}
}
class Child extends Parent{
#Override
void A(){}
}
Parent p = new Parent();
Child c = (Child) p;
How does this work behind the scene? Does it create a new instance of Child? And also, what happens if you try to cast:
Child b = (Child) new Object();
And last one, when casting a primitive to a wrapper class:
Double d = (Double) 3.3;
I know you don't necessary need to cast it, but what if you do? Is there anything significant that happens on the backend?
No new objects are created in the system when you use explicit casting (except in your last case, where you cast a primitive type to an object wrapper, since double is not an object like Double is). Note that this explicit cast isn't necessary due to Java's autoboxing feature.
In your (Child) new Object() scenario, you will receive a ClassCastException because an Object is not a Child (although the opposite is true).
The answer to your first scenario is the most complicated. Essentially the parent class is treated like an interface might be. When you cast the Child to the Parent, only the Parent API is available. However, the overridden method will still be called. So, if you do:
Parent p = (Parent) new Child();
p.a();
... the Child's public void a() will be called, even though it is being seen through the lens of the Parent class. However if you were to have a second method in the Child that the Parent does not have (let's say public void b() for instance), you would not be able to call that without casting the object back to a Child.
"Behind the scenes", as you say, the only new thing that is created is another object reference which points to the same object. You can have as many references as you like to the same, singular object. Consider this example:
Parent p = new Parent();
Parent p1 = p;
Parent p2 = p;
Parent p3 = p2;
Here, there are four references (p, p1, p2, and p3) each of which points to the same object you created with the new Parent() declaration.
I would probably argue on the philosophical point, though, that this creation of new references is actually explicit rather than behind the scenes when you say Parent p = something.
Links:
http://en.wikipedia.org/wiki/Primitive_wrapper_class
http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html
http://docs.oracle.com/javase/7/docs/api/java/lang/ClassCastException.html
http://docs.oracle.com/javase/tutorial/java/IandI/override.html
Is Java "pass-by-reference" or "pass-by-value"?
The simple answer to your main question is No.
All casting happens at syntax checking time.
Casting affects how the syntax checker looks at the object,
it does not affect the object itself, a Child cast to be a
Parent, is still a Child.
However the cast is only checked at Runtime.
Which is why it is dangerous and should not be used
unless there is no other way.
Accodring to this: checkcast, what it does is to verify if the reference is assignable. If it is, the stack is not changed and the operation on that reference are kept.
So if you have:
Child c = ( Child ) anyObject;
c.sayHi();
If the cast success, then the method sayHi could be invoked:
If objectref can be cast to the resolved class, array, or interface type, the operand stack is unchanged; otherwise, the checkcast instruction throws a ClassCastException.
Here's the "bytecode"
$ cat CastDemo.java
class Parent {}
class Child extends Parent {}
class Main {
Child c = (Child) new Parent();
}
$ javap -c Main
Compiled from "CastDemo.java"
class Main {
Child c;
Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #2 // class Parent
8: dup
9: invokespecial #3 // Method Parent."<init>":()V
12: checkcast #4 // class Child
15: putfield #5 // Field c:LChild;
18: return
}
First of all, be very careful not to confuse conversion with casting. They may share the surface syntax, but are very different processes.
In Java you can downcast an Object to any type, but at runtime you'll get a ClassCastException if the object is not in fact compatible with the target type. This happens at the bytecode level: there is a bytecode instruction dedicated to downcasting.
Child c = (Child) new Object();
will unconditionally result in a ClassCastException.
Double d = 3.3; // note: no explicit casting needed
will perform autoboxing into an instance of Double. So here, a new instance is actually created.
A normal, successful dowcast may look like this:
Object o = "a";
String s = (String)o;
Here, no objects are created: only the value of o is copied into s. The value is a reference.
Downcasting an object is not doing anything to that object. Behind the scenes the compiler will inject checkcast bytecode operation. If p is not really an instance of Child, exception will be thrown. Otherwise you basically have a (type-)safe reference to the same object with different, more specific type.
Child b = (Child) new Object();
This fails with ClassCastException. JVM compares getClass() of new Object() with Child.class. Since Object.class is not a subclass of Child.class, exception is thrown.
Double d = (Double) 3.3;
Here, casting isn't even necessary, this works as well: Double d = 3.3. Behind the scenes this is translated to:
Double d = Double.valueOf(3.3);
This is known as Autoboxing.