ClassCastException vs "Incompatible types" in Java - java

I've been working on studying for the OCJA8 Java exam and started reading about Exceptions, especially about ClassCastException. I realized I have some trouble in identifying whether it's a good cast, a ClassCastException or a compilation error with the message "incompatible types".
As far as I understood, "incompatible types" compilation error is going to result when trying to cast from a class to an unrelated class (for example, from String to Integer. String isn't neither a subclass, nor a superclass of Integer, so they are unrelated). Such casting does, indeed, result in a compilation error.
Regarding ClassCastException, I'm not sure when it actually happens. Tried reading about it in Boyarsky and Selikoff's OCJA8 book, but still don't have a proper idea of when it happens.
What I know, for sure, is that when I'm trying to cast from a subclass to a superclass, it works. I thought that might happen because the subclass inherits every method/variable of the superclass, so no issues will happen.
I'm still confused about when ClassCastException happens, compared to the "incompatible types" compilation error. Shouldn't this code also result in a runtime exception?
class A {}
class B extends A {}
public class Main {
public static void main(String[] args) {
A a = new A();
B b = a;
}
}
It doesn't, though. I receive a compilation error. Seems that I don't know when, what happens and can't seem to find it anywhere.

The cast operator looks like this: (Type) expression.
It used for 3 completely unrelated things, and due to the way java works, effectively, a 4th and 5th thing, though it's not the cast operation itself that causes it, it's merely a side-effect. A real guns and grandmas situation. Just like + in java means 2 entirely unrelated things: Either numeric addition, or string concatenation.
Hence, you shouldn't ever call it 'casting' unless you mean specifically writing 'parens, type, close parens, expression' which should rarely come up in normal conversation. Instead, call it what the effect of the cast operator actually is, which depends entirely on what you're writing.
The 5 things are:
Primitive conversion. Requires Type to be primitive and expression to also be primitive.
Type coercion. Requires Type to be non-primitive and expression to be non-primitive, and is only about the part that is not in <> (so not the generics part).
Type assertion. Requires Type to be non-primitive and contain generics, and is specifically about the generics part.
Boxing/Unboxing. Java automatically wraps a primitive into its boxed type, or unwraps the value out of a boxed type, as needed, depending on context. casting is one way to create this context.
Lambda/MethodRef selection. Lambdas/methodrefs are a compiler error unless, from context, the compiler can figure out what functional interface type the lambda/methodref is an implementation for. Casts are one way to establish this context.
The space you're currently playing in is the Type Coercion part. Note that neither type coercion nor assertion do any conversion. These do nothing at all at runtime (type assertion), or mostly nothing at all - type coercion, at runtime, either throws ClassCastEx, or does nothing. No conversion ever takes place. This doesn't work:
Number n = 5;
String s = (String) n;
One might think this results in the string "5". That's not how casting works.
What is type coercion
Type coercion casting does 2 completely separate things:
Changes the type of an expression
In java, when you invoke a method, the compiler must figure out which exact method you mean and codes that into the bytecode. If the compiler can't figure out which one you want, it won't compile. The lookup is based on a combination of the method name as well as the parameter types - specifically, the compile time type of them.
Number n = 5;
foo(n); // prints 'Number', not 'Integer'!
void foo(Number n) { System.out.println("Number variant"); }
void foo(Integer n) { System.out.println("Integer variant"); }
Hence, the type of the expression itself, as the compiler thinks of it, is important for this sort of thing. Casting changes the compile-time type. foo((Integer) n) would print 'Integer variant'.
Check if its actually true
The second thing type coercion does, is generate bytecode that checks the claim. Given:
Number n = getNumber();
Integer i = (Integer) n;
Number getNumber() {
return new Double(5.5); // a double!
}
Then clearly we can tell: That type cast is not going to work out, n is not, in fact, pointing at an instance of Integer at all. However, at compile time we can't be sure: We'd have to go through the code of getNumber to know, and given the halting problem, it's not possible for arbitrary code to be analysed like this. Even if it was, maybe tomorrow this code changes - signatures are set, but implementations can change.
Thus, the compiler will just let you write this, but will insert code that checks. This is the CHECKCAST bytecode instruction. That instruction does nothing if the cast holds (the value is indeed pointing at an object of the required type), or, if the object it is pointing at isn't, then a ClassCastException is thrown. Which should probably be called TypeCoercionException instead, and the bytecode should probably be called CHECKTYPE.
compiler error 'incompatible types' vs ClassCastEx
A type coercion cast comes in 3 flavours. That 'change the compile time type of the expression' thing is common to all 3. But about the check if it's actually true thing, you have 3 options:
It is always true
This seems pointless:
Integer i = 5;
Number n = (Number) i;
And it is - any linting tool worth its salt will point out this cast does absolutely nothing at all. The compiler knows it does nothing (all integers are also numbers, doing a runtime check is useless), and doesn't even generate the CHECKCAST bytecode. However, sometimes you do this solely for the fact that the type changes:
Integer i = 5;
foo((Number) i); // would print 'Number variant', even though its an integer.
Point is, this cast, while usually pointless, is technically legal; java just lets it happen and doesn't even generate the CHECKCAST. It cannot possibly throw anything at runtime.
It is always false
Integer i = 5;
Double d = (Double) i;
At compile time the compiler already knows this is never going to work. No type exists that it both Integer and Double. Technically, null would work, but nevertheless the java spec dictates that the compiler must reject this code, and fail with a 'incompatible types' compiler error. There are other ways to make the compiler emit this error message; this is just one of them.
The check may be true or false
In which case the compiler compiles it and adds a CHECKCAST bytecode instruction so that at runtime the type is checked. This could result in a ClassCastException.
The other way to get CCEx
generics are entirely a compile time affair. The runtime has no idea what they mean. That means that this code:
List<String> list = getListOfStrings();
list.get(0).toLowerCase();
is compiled to:
List list = getListOfStrings();
((String) list.get(0)).toLowerCase();
The compiler injects a cast (and as the generics-erased List's get method returns Object, the test could pass, or fail, a CHECKCAST bytecode instruction is generated, which could throw ClassCastEx). This means you can cast ClassCastExceptions on lines with no casts, but then it does mean someone messed up their generics and ignored a compile time warning. This method would do the job:
public List<String> getListOfStrings() {
var broken = new ArrayList<Number>();
broken.add(5); // not a string
List raw = broken; // raw type.
return (List<String>) raw;
}

B class is A type, but A not is B.

Related

How java determine where to insert the "type cast" in type erasure process [duplicate]

This question already has answers here:
When is generic return value of function casted after type erasure?
(3 answers)
Closed 5 years ago.
Consider below code and the output in Eclipse 4.5.0 & javac(1.8) .
I know it is due to the type erasure in runtime, but why the second one still output data even it is declared as List of Integer, I also checked with javap, checkcast bytecode is only inserted in the third output .
My questions are :
is it a bug ?
How the javac determine where to insert the "cast" ?
public static void main(String[] args){
List<String> a = Arrays.asList("abc","def");
List<Integer> b = (List<Integer>)(List<?>)a;
System.out.println(b.size()); --output 2
System.out.println(b.get(1)); ---output "def"
System.out.println(b.get(1).getClass()); --error in type cast
EDIT
Checked the answer below and the When is generic return value of function casted after type erasure? , quite similar with my case.
If we add the answer from this What is meant by "the erasure of the static type of the expression on which it is called" in the getClass() docs?, then it will be much clear about the "cast" rule.
The compiler can determine where to insert the cast and ensure type safety.
My first case is fine since it will return int anyway.
Second case is fine since the println expect Object. so no cast is needed to ensure type safety.
Thrid case is not since the getClass() is expected to return the Class which is the static type of b.get(1) according to JLS. So the cast is inserted and get type cast error.
As #newacct said , "you should not rely on the compiler to decide either way" (when there is alternative choice and also ensure type safety ,the 2nd case here).
In the last example, the cast happens as follows
Class clazz = ((Integer)b.get(1)).getClass();
And hence the exception, where the second line
System.out.println(b.get(1));
Does not assign to an integer and hence the cast wont happen, assign it to an integer to see it failing.
Integer x = b.get(1); //fails

Why is it possible to compare incompatible types by reference in Java?

Check out this snippet:
List<Integer> c = new ArrayList<>();
Map<String,Boolean> m = new HashMap<>();
if( c == m ) //no error here! WHY?
{
c = m; //"Incompatible types" error, as expected.
m = c; //"Incompatible types" error, as expected.
}
How come c == m gives no error?
I am using the javac of jdk1.8.0.20 and I have no reason to suspect that it disregards the java language specification, so this is with a fairly absolute level of certainty in the spec, so:
What is the point / purpose / usefulness of having something like this allowed by the spec?
Just because the types are inconvertible doesn't mean that are not equal objects. If the types are "Inconvertible" it means a cast is required to check the type is actually convertible.
interface Outputer extends Consumer<String>, Serializable { }
Outputer out = System.out::println;
Consumer<String> cs = out;
Serializable s = out;
System.out.println(s == cs); // prints true
// s = cs; // Inconvertible types, doesn't compile
s = (Serializable) cs; // compiles and runs fine.
cs and s are inconvertible types, yet they point to the same object and this prints true
It's permitted specifically because List and Map are interfaces.
We could imagine some class
// (please only imagine)
class ListMap implements List, Map {...}
Compile-time legality of reference equality (15.21.3) is the same as that of reference type casting (5.5.1). In short, since you can generally cast between any reference type and an interface, you can also generally compare reference equality of any type to an interface.
The permission seems more useful in the context of smaller interfaces like Comparable, Serializable, Iterable, etc., where a class is more likely to implement more than one.
The Incompatible types error appears because when the assignment is being triggered, an operation called Assignment conversion starts. What it does is:
Assignment conversion occurs when the value of an expression is assigned (ยง15.26) to a variable: the type of the expression must be converted to the type of the variable.
If the conversion fails, then a compile-time error is thrown.
However, an compile-time error is not thrown when the == operator takes place.
In Java, whenever you have objects, the system uses pointers. And when you use == on two objects, it compares their pointers. In other words, it checks whether the two pointers are pointing to the same object in memory. This is always a safe check to make.
Also, it should be noted that when inheritance (and polymorphism) is involved, it is possible to have multiple pointers of different types that point to the same object. Of course, that wasn't the case in your example. But as I said earlier, it is harmless to check whether two pointers are pointing to the same object since that check makes no assumptions about the classes involved.
When you write
if(c==m)
you are simply checking for #hashcode Which might be same for two object under certain circumstances so you might not be getting any error in that line!

Why does Java's type erasure not break this?

public class Test {
public static class Nested<T> {
public T val;
Nested(T val) { this.val = val; }
}
public static void main(String[] args) {
Nested<Integer> a = new Nested<Integer>(5);
Nested<Integer> b = new Nested<Integer>(2);
Integer diff = a.val - b.val;
}
}
The above code works fine. However, if I add a method to Nested:
T diff(Nested<T> other) { return this.val - other.val; }
I get a compilation error:
operator - cannot be applied to T,T
This makes sense to me. The type of T gets erased at runtime, so Java can't apply an operator that's only defined for certain classes like Integer. But why does a.val - b.val work?
Edit:
Lots of good answers. Thanks everyone. The gist of it, if I understand correctly, is that the compiler can add casts to Integer in a.val - b.val because it knows a and b were instantiated as as Nested<Integer>. However, since this.val - other.val occurs inside the body of a generic function definition (where T still could be anything), the compiler cannot add the casts that would be necessary to make "-" work. This leads to a more interesting question, namely, if the Java compiler were capable of inlining, would it be possible for a generic function like diff to work?
The difference between the two is whether you are inside a generic method or you are outside of it.
You got it absolutely right that inside the method T is not known to be an Integer, so operator minus - cannot be applied. However, when you are in main(), outside the generic method, the compiler knows that you've instantiated Nested with Integer, so it knows very well how to apply the operator. Even though the implementation of the generic has erased the type to produce the code for Nested<T>, the compiler does not think of a and b in terms of Nested<T>: it has enough knowledge to insert an appropriate cast, unbox the results, and apply the minus - operator.
You are getting a compile-time error, not a runtime one.
public static void main(String[] args) {
Nested<Integer> a = new Nested<Integer>(5);
Nested<Integer> b = new Nested<Integer>(2);
Integer diff = a.val - b.val;
}
Here, compiler knows that both T are Integer. You just declared <Integer>.
T diff(Nested<T> other) { return this.val - other.val; }
Here, compiler is not certain about T. It could be anything. And, numeric only operator - is not allowed for just anything.
a.val - b.val works because it is validated by the compiler, not in runtime. The compiler "sees" that you're using <Integer> and it compiles and runs Ok, in runtime there is no problem even with erasure because the compiler already validated that.
Because the code doesn't live within Nested, the type is known. The compiler can clearly see that a.val - b.val is an Integer minus an Integer, which can be auto-boxed. The compiler essentially rewrites it to
Integer diff = Integer.valueOf(((Integer) a.val).intValue() - ((Integer) b.val).intValue())
The .intValue and .valueOf calls are from the auto-boxing and auto-unboxing.
The type casts are safe for the compiler to insert because you used a parameterized type Nested.
True, technically, a could be something else, like a Calendar object, since the type is unknown at runtime. But if you are using generics, the compiler trusts that you aren't doing anything dumb to circumvent it. Therefore, if a.val or b.val were anything other than Integers, a ClassCastException would be thrown at runtime.
Because method call is at runtime and a.val - b.val is checked at compile time.
In first case, compiler knows that the type is Integer and -
operation is allowed for integers.
In second case, the type of T is not known to the compiler in advance, hence it is not sure whether - operation is valid or not. Hence the compiler error.
Consider we use the method as diff(Nested<Book> other) so there is no way a book can be subtracted from other.

Casting variables in Java

I wonder if anyone could tell me how casting works? I understand when I should do it, but not really how it works. On primitive data types I understand partially but when it comes to casting objects I don't understand how it works.
How can a object with the type Object just suddenly be cast to, let's say, MyType (just an example) and then get all the methods?
Casting in Java isn't magic, it's you telling the compiler that an Object of type A is actually of more specific type B, and thus gaining access to all the methods on B that you wouldn't have had otherwise. You're not performing any kind of magic or conversion when performing casting, you're essentially telling the compiler "trust me, I know what I'm doing and I can guarantee you that this Object at this line is actually an <Insert cast type here>." For example:
Object o = "str";
String str = (String)o;
The above is fine, not magic and all well. The object being stored in o is actually a string, and therefore we can cast to a string without any problems.
There's two ways this could go wrong. Firstly, if you're casting between two types in completely different inheritance hierarchies then the compiler will know you're being silly and stop you:
String o = "str";
Integer str = (Integer)o; //Compilation fails here
Secondly, if they're in the same hierarchy but still an invalid cast then a ClassCastException will be thrown at runtime:
Number o = new Integer(5);
Double n = (Double)o; //ClassCastException thrown here
This essentially means that you've violated the compiler's trust. You've told it you can guarantee the object is of a particular type, and it's not.
Why do you need casting? Well, to start with you only need it when going from a more general type to a more specific type. For instance, Integer inherits from Number, so if you want to store an Integer as a Number then that's ok (since all Integers are Numbers.) However, if you want to go the other way round you need a cast - not all Numbers are Integers (as well as Integer we have Double, Float, Byte, Long, etc.) And even if there's just one subclass in your project or the JDK, someone could easily create another and distribute that, so you've no guarantee even if you think it's a single, obvious choice!
Regarding use for casting, you still see the need for it in some libraries. Pre Java-5 it was used heavily in collections and various other classes, since all collections worked on adding objects and then casting the result that you got back out the collection. However, with the advent of generics much of the use for casting has gone away - it has been replaced by generics which provide a much safer alternative, without the potential for ClassCastExceptions (in fact if you use generics cleanly and it compiles with no warnings, you have a guarantee that you'll never get a ClassCastException.)
Actually, casting doesn't always work. If the object is not an instanceof the class you're casting it to you will get a ClassCastException at runtime.
Suppose you wanted to cast a String to a File (yes it does not make any sense), you cannot cast it directly because the File class is not a child and not a parent of the String class (and the compiler complains).
But you could cast your String to Object, because a String is an Object (Object is parent). Then you could cast this object to a File, because a File is an Object.
So all you operations are 'legal' from a typing point of view at compile time, but it does not mean that it will work at runtime !
File f = (File)(Object) "Stupid cast";
The compiler will allow this even if it does not make sense, but it will crash at runtime with this exception:
Exception in thread "main" java.lang.ClassCastException:
java.lang.String cannot be cast to java.io.File
Casting a reference will only work if it's an instanceof that type. You can't cast random references. Also, you need to read more on Casting Objects.
e.g.
String string = "String";
Object object = string; // Perfectly fine since String is an Object
String newString = (String)object; // This only works because the `reference` object is pointing to a valid String object.
The right way is this:
Integer i = Integer.class.cast(obj);
The method cast() is a much safer alternative to compile-time casting.

Does Java casting introduce overhead? Why?

Is there any overhead when we cast objects of one type to another? Or the compiler just resolves everything and there is no cost at run time?
Is this a general things, or there are different cases?
For example, suppose we have an array of Object[], where each element might have a different type. But we always know for sure that, say, element 0 is a Double, element 1 is a String. (I know this is a wrong design, but let's just assume I had to do this.)
Is Java's type information still kept around at run time? Or everything is forgotten after compilation, and if we do (Double)elements[0], we'll just follow the pointer and interpret those 8 bytes as a double, whatever that is?
I'm very unclear about how types are done in Java. If you have any reccommendation on books or article then thanks, too.
There are 2 types of casting:
Implicit casting, when you cast from a type to a wider type, which is done automatically and there is no overhead:
String s = "Cast";
Object o = s; // implicit casting
Explicit casting, when you go from a wider type to a more narrow one. For this case, you must explicitly use casting like that:
Object o = someObject;
String s = (String) o; // explicit casting
In this second case, there is overhead in runtime, because the two types must be checked and in case that casting is not feasible, JVM must throw a ClassCastException.
Taken from JavaWorld: The cost of casting
Casting is used to convert between
types -- between reference types in
particular, for the type of casting
operation in which we're interested
here.
Upcast operations (also called
widening conversions in the Java
Language Specification) convert a
subclass reference to an ancestor
class reference. This casting
operation is normally automatic, since
it's always safe and can be
implemented directly by the compiler.
Downcast operations (also called
narrowing conversions in the Java
Language Specification) convert an
ancestor class reference to a subclass
reference. This casting operation
creates execution overhead, since Java
requires that the cast be checked at
runtime to make sure that it's valid.
If the referenced object is not an
instance of either the target type for
the cast or a subclass of that type,
the attempted cast is not permitted
and must throw a
java.lang.ClassCastException.
For a reasonable implementation of Java:
Each object has a header containing, amongst other things, a pointer to the runtime type (for instance Double or String, but it could never be CharSequence or AbstractList). Assuming the runtime compiler (generally HotSpot in Sun's case) cannot determine the type statically a some checking needs to be performed by the generated machine code.
First that pointer to the runtime type needs to be read. This is necessary for calling a virtual method in a similar situation anyway.
For casting to a class type, it is known exactly how many superclasses there are until you hit java.lang.Object, so the type can be read at a constant offset from the type pointer (actually the first eight in HotSpot). Again this is analogous to reading a method pointer for a virtual method.
Then the read value just needs a comparison to the expected static type of the cast. Depending upon instruction set architecture, another instruction will need to branch (or fault) on an incorrect branch. ISAs such as 32-bit ARM have conditional instruction and may be able to have the sad path pass through the happy path.
Interfaces are more difficult due to multiple inheritance of interface. Generally the last two casts to interfaces are cached in the runtime type. IN the very early days (over a decade ago), interfaces were a bit slow, but that is no longer relevant.
Hopefully you can see that this sort of thing is largely irrelevant to performance. Your source code is more important. In terms of performance, the biggest hit in your scenario is liable to be cache misses from chasing object pointers all over the place (the type information will of course be common).
For example, suppose we have an array of Object[], where each element might have a different type. But we always know for sure that, say, element 0 is a Double, element 1 is a String. (I know this is a wrong design, but let's just assume I had to do this.)
The compiler does not note the types of the individual elements of an array. It simply checks that the type of each element expression is assignable to the array element type.
Is Java's type information still kept around at run time? Or everything is forgotten after compilation, and if we do (Double)elements[0], we'll just follow the pointer and interpret those 8 bytes as a double, whatever that is?
Some information is kept around at run time, but not the static types of the individual elements. You can tell this from looking at the class file format.
It is theoretically possible that the JIT compiler could use "escape analysis" to eliminate unnecessary type checks in some assignments. However, doing this to the degree you are suggesting would be beyond the bounds of realistic optimization. The payoff of analysing the types of individual elements would be too small.
Besides, people should not write application code like that anyway.
The byte code instruction for performing casting at runtime is called checkcast. You can disassemble Java code using javap to see what instructions are generated.
For arrays, Java keeps type information at runtime. Most of the time, the compiler will catch type errors for you, but there are cases where you will run into an ArrayStoreException when trying to store an object in an array, but the type does not match (and the compiler didn't catch it). The Java language spec gives the following example:
class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
public static void main(String[] args) {
ColoredPoint[] cpa = new ColoredPoint[10];
Point[] pa = cpa;
System.out.println(pa[1] == null);
try {
pa[0] = new Point();
} catch (ArrayStoreException e) {
System.out.println(e);
}
}
}
Point[] pa = cpa is valid since ColoredPoint is a subclass of Point, but pa[0] = new Point() is not valid.
This is opposed to generic types, where there is no type information kept at runtime. The compiler inserts checkcast instructions where necessary.
This difference in typing for generic types and arrays makes it often unsuitable to mix arrays and generic types.
In theory, there is overhead introduced.
However, modern JVMs are smart.
Each implementation is different, but it is not unreasonable to assume that there could exist an implementation that JIT optimized away casting checks when it could guarantee that there would never be a conflict.
As for which specific JVMs offer this, I couldn't tell you. I must admit I'd like to know the specifics of JIT optimization myself, but these are for JVM engineers to worry about.
The moral of the story is to write understandable code first. If you're experiencing slowdowns, profile and identify your problem.
Odds are good that it won't be due to casting.
Never sacrifice clean, safe code in an attempt to optimize it UNTIL YOU KNOW YOU NEED TO.

Categories

Resources