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.
Related
The following simple code snippet is working fine and is accessing a static field with a null object.
final class TestNull
{
public static int field=100;
public TestNull temp()
{
return(null);
}
}
public class Main
{
public static void main(String[] args)
{
System.out.println(new TestNull().temp().field);
}
}
In the above code, the statement System.out.println(new TestNull().temp().field); in which the static field field is being associated with a NULL objectnew TestNull().temp(), still it is returning a correct value of it which is 100 instead of throwing a null pointer exception in Java! Why?
As opposed to regular member variables, static variables belong to the class and not to the instances of the class. The reason it works is thus simply because you don't need an instance in order to access a static field.
In fact I'd say it would me more surprising if accessing a static field could ever throw a NullPointerException.
If you're curious, here's the bytecode looks for your program:
// Create TestNull object
3: new #3; //class TestNull
6: dup
7: invokespecial #4; //Method TestNull."<init>":()V
// Invoke the temp method
10: invokevirtual #5; //Method TestNull.temp:()LTestNull;
// Discard the result of the call to temp.
13: pop
// Load the content of the static field.
14: getstatic #6; //Field TestNull.field:I
This is described in the Java Language Specification, Section 15.11.1: Field Access Using a Primary. They even provide an example:
The following example demonstrates that a null reference may be used to access a class (static) variable without causing an exception:
class Test {
static String mountain = "Chocorua";
static Test favorite(){
System.out.print("Mount ");
return null;
}
public static void main(String[] args) {
System.out.println(favorite().mountain);
}
}
It compiles, executes, and prints:
Mount Chocorua
Static variables are shared among every object of a class. So while the actual object reference you are returning is null (In C++: TestNull* temp = null;), you have an object of type TestNull which Java can use to find static values of that class.
Remember in Java objects are really pointers. Pointers have a type. From that type Java can discern certain information, even if its pointing to null.
Static fields are associated with the class not an instance of that class. Therefore you don't need an instance of an object to access the static field.
You could also access the field by calling TestNull.field
Given the following enum:
enum Repeat {
Daily,
Weekly,
Yearly
}
I realize we are able to write it this way:
Repeat repeat = Repeat.Daily.Weekly.Yearly.Weekly;
which is equivalent to:
Repeat repeat = Repeat.Weekly;
May I know why such syntax is allowed? Is there a way to let the compiler warn us against this?
This is allowed as Daily, Weekly, Yearly are the static field by default inside the enum and holds the object of Repeat. Also, you will get a warning from the compiler "The static field Repeat.Weekly should be accessed in a static way". It is similar to below lines of code.
class Foo{
public static Foo obj1 = new Foo();
public static Foo obj2 = new Foo();
public static Foo obj3 = new Foo();
}
Foo f = Foo.obj1.obj2.obj3; // will work fine but you will get a warning from the compiler.
Here is some part of bytecode inspection of Repeat enum and from this, it is clear that Enum variable is static and holds the Object of Enum itself.
0: new #1 // class com/java8/demo/Repeat
3: dup
4: ldc #14 // String Daily
6: iconst_0
7: invokespecial #15 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #19 // Field Daily:Lcom/java8/demo/Repeat;
13: new #1 // class com/java8/demo/Repeat
Enum instance are just static instance of the enum class.
We have two way to access static field of a class:
Via class itselft: Repeat.Daily
Via instance of class: Repeat.Daily.Daily
When you chain your enum:
Repeat repeat = Repeat.Daily.Weekly.Yearly.Weekly;
It's just like get a static field from an instance of a class.
Is there a way to let compiler warn us against this?
Yes, use a good IDE and turn on warning. That way, you'll be notified as soon as your write the code, before you even compile it.
E.g. in Eclipse, it is called "Non-static access to static member":
Enum literals are static members, and with each static member, one can access them either using the class reference:
TypeName.staticMember
TypeName.staticMethod()
Or on an instance:
new TypeName().staticMember
new TypeName().staticMethod()
The second approach is discouraged (and the compiler will issue a warning)
As enum literals are just static members, Repeat.Daily.Weekly.Yearly.Weekly is like the second code snippet above, accessing static members on instance references.
With a class, that would be:
class Type {
static Type INSTANCE1, INSTANCE2, INSTANCE3;
}
And one can get a reference to INSTANCE3 using Type.INSTANCE1.INSTANCE2.INSTANCE3. It's valid, but it's bad practice.
This question already has answers here:
Casting to generic type in Java doesn't raise ClassCastException?
(5 answers)
Closed 7 years ago.
I have the following class (simplified but still a working example):
class Test<T> {
List<T> l = new ArrayList<>();
public Test() {
}
public void add(Object o) {
l.add((T)o);
}
}
And the test code:
Test<Double> t = new Test<>();
t.add(1);
t.add(1.2);
t.add(-5.6e-2);
t.add("hello");
Everything is working fine, and that's not what I was expecting. Shouldn't the add method throw a ClassCastException? If I add a get method that's more or less the same thing:
public T get(int i) {
return l.get(i);
}
.../...
t.get(1); // OK.
t.get(3); // OK (?)
Double d = t.get(3); // throws ClassCastException
Why is it only on variable assignment that the exception is thrown? How can I enforce type consistency if the (T) cast doesn't work?
Shouldn't the add method throw a ClassCastException?
No, it shouldn't (although I wish it did). Long story short, Java implementation of generics discards type information after compiling your code, so List<T> is allowed to take any Object, and the cast inside your add method is not checked.
Why is it only on variable assignment that the exception is thrown?
Because the cast to Double there is inserted by the compiler. Java compiler knows that the return type of get is T, which is Double, so it inserts a cast to match the type of the variable d, to which the result is being assigned.
Here is how you can implement a generic-safe cast:
class Test<T> {
private final Class<T> cl;
List<T> l = new ArrayList<>();
public Test(Class<T> c) {
cl = c;
}
public void add(Object o) {
l.add(cl.cast(o));
}
}
Now the cast is performed by a Class<T> object, so you will get a ClassCastException on an attempt to insert an object of an incorrect type.
As an alternative solution you can use Collections.checkedList:
class Test<T> {
List<T> l;
public Test(Class<T> c) {
l = Collections.checkedList(new ArrayList<T>(), c);
}
public void add(Object o) {
l.add((T) o);
}
}
This way you will get the following exception:
Exception in thread "main" java.lang.ClassCastException: Attempt to insert
class java.lang.Integer element into collection with element type class java.lang.Double
at java.util.Collections$CheckedCollection.typeCheck(Collections.java:3037)
at java.util.Collections$CheckedCollection.add(Collections.java:3080)
at Test.add(Test.java:13)
For the completeness of this resource, here's the difference in compiled bytecode between a cast to a generic:
public void add(java.lang.Object);
Code:
0: aload_0
1: getfield #4 // Field l:Ljava/util/List;
4: aload_1
5: invokeinterface #7, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
10: pop
11: return
And an explicit cast to a Double with no generics:
public void add(java.lang.Object);
Code:
0: aload_0
1: getfield #4 // Field l:Ljava/util/List;
4: aload_1
5: checkcast #7 // class java/lang/Double
8: invokeinterface #8, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
13: pop
14: return
You can see that the version with generics doesn't perform the checkcast instruction at all (thanks to type erasure, so you shouldn't expect an exception when giving it data with an unmatched class. It's unfortunate that this isn't more strictly enforced, but this makes sense as generics are for making stricter compile-time type checks, and aren't much help at runtime due to type erasure.
Java will check the types of function arguments to see if there is a type match, or if a type promotion can be performed. In your case, String is the type of the argument, and that can be promoted to Object, which is the extent of the compile-time type checks that ensure that function call works.
There are a few options, and dasblinkenlight's solution is probably the most elegant. (You may not be able to change the method signature, say, if you are overriding an inherited add method, or plan on passing down the add method, etc).
Another option that may help is using a bounded type parameter instead of an unbounded one. Unbounded type parameters are completely lost after compilation due to type erasure, but using a bounded type parameter will replace instances of the generic type with that/those it must extend.
class Test<T extends Number> {
Of course, T is not truly generic at this point, but using this class definition will enforce types at runtime since the cast will be checked against the Number superclass. Here's the bytecode to prove it:
public void add(java.lang.Object);
Code:
0: aload_0
1: getfield #4 // Field l:Ljava/util/List;
4: aload_1
5: checkcast #7 // class java/lang/Number
8: invokeinterface #8, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
13: pop
14: return
This class definition generates the desired ClassCastException when trying to add the string.
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.
The following simple code snippet is working fine and is accessing a static field with a null object.
final class TestNull
{
public static int field=100;
public TestNull temp()
{
return(null);
}
}
public class Main
{
public static void main(String[] args)
{
System.out.println(new TestNull().temp().field);
}
}
In the above code, the statement System.out.println(new TestNull().temp().field); in which the static field field is being associated with a NULL objectnew TestNull().temp(), still it is returning a correct value of it which is 100 instead of throwing a null pointer exception in Java! Why?
As opposed to regular member variables, static variables belong to the class and not to the instances of the class. The reason it works is thus simply because you don't need an instance in order to access a static field.
In fact I'd say it would me more surprising if accessing a static field could ever throw a NullPointerException.
If you're curious, here's the bytecode looks for your program:
// Create TestNull object
3: new #3; //class TestNull
6: dup
7: invokespecial #4; //Method TestNull."<init>":()V
// Invoke the temp method
10: invokevirtual #5; //Method TestNull.temp:()LTestNull;
// Discard the result of the call to temp.
13: pop
// Load the content of the static field.
14: getstatic #6; //Field TestNull.field:I
This is described in the Java Language Specification, Section 15.11.1: Field Access Using a Primary. They even provide an example:
The following example demonstrates that a null reference may be used to access a class (static) variable without causing an exception:
class Test {
static String mountain = "Chocorua";
static Test favorite(){
System.out.print("Mount ");
return null;
}
public static void main(String[] args) {
System.out.println(favorite().mountain);
}
}
It compiles, executes, and prints:
Mount Chocorua
Static variables are shared among every object of a class. So while the actual object reference you are returning is null (In C++: TestNull* temp = null;), you have an object of type TestNull which Java can use to find static values of that class.
Remember in Java objects are really pointers. Pointers have a type. From that type Java can discern certain information, even if its pointing to null.
Static fields are associated with the class not an instance of that class. Therefore you don't need an instance of an object to access the static field.
You could also access the field by calling TestNull.field