Why this is a compile-time error when Java does the Autoboxing? Am I missing something?
int primitiveIntVariable = 0;
if (primitiveIntVariable instanceof Integer) {
}
I get
Inconvertible types; cannot cast 'int' to 'java.lang.Integer'
As the name suggests, instanceof means an instance (object) of a class. Primitive datatypes are not instances.
This is how you get the class for a primitive datatype:
int i = 1;
System.out.println(((Object)i).getClass().getName());
// prints: java.lang.Integer
So instead of instanceof, use isInstance(...) like this:
Integer.class.isInstance(1); // returns true
Integer.class.isInstance(1.2); // returns false
Hope this helps. Good luck.
int cannot be anything but an int, so the entire concept of using instanceof is meaningless, and misguided.
You only use instanceof if you need to check if the actual object is a particular type different from the type of the variable in question (or known supertypes hereof).
E.g. if the declared type of the variable (or the return value, or the compile-type of expression), is Integer, then it makes no sense whatsoever to check it is an instanceof of Integer. Given that Java is type-safe, you already know that it is.
Similarly, it makes no sense whatsoever to check if a known Integer is an instanceof of Number. You already know that it is.
And it makes even less sense to check if a known int is an instanceof of Integer. It's a primitive, and you know it is, so there is absolutely no way it can be an instance of any object type.
The last will generate a compiler error. The first two examples are compiler warnings, which is very evident if you use any good IDE. Always use a good IDE, because they catch so many dumb mistakes we all happen to write occasionally.
So the above was an explanation of why it makes no sense to even try, but even though integerVar instanceof Integer makes no sense, it compiles ok, but intVar instanceof Integer fails to compile, so why it that?
The reason is actually related to this mistaken statement in the question:
when Java does the Autoboxing
Java doesn't do autoboxing everywhere. Autoboxing only happens in:
assignment contexts, e.g. Integer x = 5
invocation contexts, e.g. foo(5) where parameter is an Integer
casting contexts, e.g. (Integer) 5 or (Object) 5
It does not happen by itself in the middle of an expression, and instanceof is an expression operator.
But, more specifically, it fails to compile because JLS 15.20.2. Type Comparison Operator instanceof says so:
RelationalExpression instanceof ReferenceType
The type of the RelationalExpression operand of the instanceof operator must be a reference type or the null type, or a compile-time error occurs.
Related
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.
I am reading up OCaml and from wiki, it says:
*its static type system renders runtime type mismatches impossible*
I understand why, but then I think, why is this so special in OCaml (and FP)? How do you cause a runtime type mismatch in, say, Java? e.g.
boolean a = true;
int b = a + 1;
will return an error in compile time.
EDIT 1:
Haskell
func :: Int -> Bool
func i = if i > 0 then True else False
Java
boolean func (int i) {
if (i > 0) return true; else return false;
}
Isn;t it the case that both will guarantee the argument type when the func is called?
In Java, you can cause a Runtime type mismatch like this:
Object i = Integer.valueOf(6);
String s = (String) i;
System.out.println(s);
This will compile, because the compile-time type of i (Object) is allowed to be cast to String, however at Runtime, the actual value of i (6, as Integer) will be incompatible to String.
Given this, a ClassCastException is thrown.
Consider the following code using arrays:
// create an array of strings
String[] strings = new String[10];
// cast it to an array of objects
Object[] objects = strings;
// insert an object into the array
objects[0] = new Object(); // Run-time error occurs here
Java allows this to compile, despite the fact that casting a array of strings to an array of objects array introduces the possibility of run-time errors. Line 8 demonstrates this, causing a run-time exception of a type created specifically for this situation: java.lang.ArrayStoreException: java.lang.Object.
See Java generics and type erasure
That wiki is discussing static type systems in general, and contrasting them with dynamically-typed languages rather than other statically-typed languages. There's nothing specific to OCaml or Haskell about runtime type mismatches that doesn't apply to all statically-typed languages.
Note that impossible is a little disingenuous. Pretty much all statically-typed languages give you the ability to also do runtime typing in a limited way, because certain tasks are extremely difficult without it. In fact, the very paragraph you're quoting lists a couple of those cases, like serialization. Other answers here have provided some good examples in Java. However, the vast majority of your code should be able to easily avoid runtime type mismatches.
In Java, type mismatches are possible. For example, the following throws a ClassCastException.
Object o = 1;
String s = (String) o;
However, the arguments passed to a method are checked by the compiler.
It is impossible to invoke a method with signature
boolean func (int i)
unless i is an int, and it is impossible to invoke a method with signature
boolean func2 (String s)
unless s is a String or null.
Therefore you will never get a ClassCastException at runtime within the body of func2 because s is not a String.
In Java, it is impossible to have a type mismatch between reifiable types (primitive types, non-generic reference types, raw types, types parameterized by all wildcards, or array types whose element type is reifiable).
If you have a variable of a reifiable type, then the value it holds at any point in time is guaranteed to be a value of that type. For reference types, this means that the reference is either null or points to an object whose runtime class is a subtype of the variable's type. This is guaranteed because Java requires a cast when storing a value whose type is not a subtype of the variable's type, and casts to reifiable types are checked casts, which means they are checked at runtime, and if the type is not compatible it will throw an exception rather than let there be a type mismatch.
On the other hand, for non-reifiable types (e.g. parameterized types), it is possible to have a type mismatch (which is called "heap pollution" in Java terminology). This is because casts to non-reifiable types are unchecked casts.
List<String> foo = new ArrayList<String>();
foo.add("hi");
List<?> bar = foo;
List<Integer> baz = (List<Integer>)bar; // unchecked cast
// now there is a type mismatch
Sorry to keep asking the basics but I don't understand this simple code and why the first print statement goes through the compiler ok and even prints true, but the second print statement doesn't compile, giving me an "incomparable types" error:
int in1 = 38;
Number Nn1 = in1;
System.out.println(in1 == Nn1);
System.out.println(Nn1 == in1);
I am not expecting this result, I thought it was pretty standard that == was symmetric?
I am using javac 1.6.0_26 and also NetBeans but get the same result, the first println statement compiles without problem and the second does not..
I believe that, according to the Java Language Specification, neither way round should compile.
It's important firstly to understand that auto(un)boxing is only applied to expressions that meet certain criteria, and only for specific wrapper classes (Integer, Long etc, not Number).
Now, in the case of ==, autounboxing is applied specifically when one is of
[primitive] numeric type and the other is convertible to [primitive] numeric type (JLS 15.12.1) according to the rules. And as we've just stated, "according to the rules", Number is not convertible to a numeric primitive type.
It is NOT, the case, for example, that the int should be converted to an Integer and then a reference comparison made: autoboxing is not specified to be applied to an == reference comparison (JLS 15.21.3).
So if your compiler is allowing the cited code to compile, it does not obey the Java Language Specification.
This behaviour makes sense because to perform a numeric comparison, the compiler needs to know the actual specific type of both operands in order to perform numeric promotion. You might think that you can compare, say, a Number with an integer, and that the compiler should just call .intValue() on the Number. But this is inappropriate, because if the original number type was actually a Float, then the correct comparison is actually to first convert the integer to a Float rather than the other way round. In other words, with a Number, the compiler doesn't have all the information to correctly perform a numeric comparison with a primitive.
My compiler (jdk1.7.0_03 on Windows) says that both lines are incorrect:
Operator == cannot be applied to int and java.lang.Number
When you check for equality between an int and an Integer, unboxing occurs.
In fact, compiler is aware that Integer operand wraps only int. It's like a clue.
However, Number, although implemented by Integer (and others), is too generic and would expect too much job for compiler to extract the original primitive type in order to operate the unboxing.
Hence, compiler complains about it and expects you a more fine-grained type.
Both lines are a compile error. If not, there's a bug in NetBeans.
If you change Number to Integer, both lines compile.
int in1 = 38;
Integer Nn1 = in1; // Changed to Integer
System.out.println(in1 == Nn1); // compiles
System.out.println(Nn1 == in1); // compiles
Because you are comparing values reference-type values with primitive values, the only way it could work would be because of an auto unboxed conversion. But that type of conversion doesn't seem to be specified in the Java Language Specification.
It probably not symmetric because it's not intended to be possible at all. Maybe a compiler bug.
Let's look at the simple Java code in the following snippet:
public class Main {
private int temp() {
return true ? null : 0;
// No compiler error - the compiler allows a return value of null
// in a method signature that returns an int.
}
private int same() {
if (true) {
return null;
// The same is not possible with if,
// and causes a compile-time error - incompatible types.
} else {
return 0;
}
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.temp());
System.out.println(m.same());
}
}
In this simplest of Java code, the temp() method issues no compiler error even though the return type of the function is int, and we are trying to return the value null (through the statement return true ? null : 0;). When compiled, this obviously causes the run time exception NullPointerException.
However, it appears that the same thing is wrong if we represent the ternary operator with an if statement (as in the same() method), which does issue a compile-time error! Why?
The compiler interprets null as a null reference to an Integer, applies the autoboxing/unboxing rules for the conditional operator (as described in the Java Language Specification, 15.25), and moves happily on. This will generate a NullPointerException at run time, which you can confirm by trying it.
I think, the Java compiler interprets true ? null : 0 as an Integer expression, which can be implicitly converted to int, possibly giving NullPointerException.
For the second case, the expression null is of the special null type see, so the code return null makes type mismatch.
Actually, its all explained in the Java Language Specification.
The type of a conditional expression is determined as follows:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
Therefore the "null" in your (true ? null : 0) gets an int type and then is autoboxed to Integer.
Try something like this to verify this (true ? null : null) and you will get the compiler error.
In the case of the if statement, the null reference is not treated as an Integer reference because it is not participating in an expression that forces it to be interpreted as such. Therefore the error can be readily caught at compile-time because it is more clearly a type error.
As for the conditional operator, the Java Language Specification §15.25 “Conditional Operator ? :” answers this nicely in the rules for how type conversion is applied:
If the second and third operands have the same type (which may be the null
type), then that is the type of the conditional expression.
Does not apply because null is not int.
If one of the second and third operands is of type boolean and the type of the
other is of type Boolean, then the type of the conditional expression is boolean.
Does not apply because neither null nor int is boolean or Boolean.
If one of the second and third operands is of the null type and the type of the
other is a reference type, then the type of the conditional expression is that
reference type.
Does not apply because null is of the null type, but int is not a reference type.
Otherwise, if the second and third operands have types that are convertible
(§5.1.8) to numeric types, then there are several cases: […]
Applies: null is treated as convertible to a numeric type, and is defined in §5.1.8 “Unboxing Conversion” to throw a NullPointerException.
The first thing to keep in mind is that Java ternary operators have a "type", and that this is what the compiler will determine and consider no matter what the actual/real types of the second or third parameter are. Depending on several factors the ternary operator type is determined in different ways as illustrated in the Java Language Specification 15.26
In the question above we should consider the last case:
Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
This is by far the most complex case once you take a look at applying capture conversion (§5.1.10) and most of all at lub(T1, T2).
In plain English and after an extreme simplification we can describe the process as calculating the "Least Common Superclass" (yes, think of the LCM) of the second and third parameters. This will give us the ternary operator "type". Again, what I just said is an extreme simplification (consider classes that implement multiple common interfaces).
For example, if you try the following:
long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));
You'll notice that resulting type of the conditional expression is java.util.Date since it's the "Least Common Superclass" for the Timestamp/Time pair.
Since null can be autoboxed to anything, the "Least Common Superclass" is the Integer class and this will be the return type of the conditional expression (ternary operator) above. The return value will then be a null pointer of type Integer and that is what will be returned by the ternary operator.
At runtime, when the Java Virtual Machine unboxes the Integer a NullPointerException is thrown. This happens because the JVM attempts to invoke the function null.intValue(), where null is the result of autoboxing.
In my opinion (and since my opinion is not in the Java Language Specification many people will find it wrong anyway) the compiler does a poor job in evaluating the expression in your question. Given that you wrote true ? param1 : param2 the compiler should determine right away that the first parameter -null- will be returned and it should generate a compiler error. This is somewhat similar to when you write while(true){} etc... and the compiler complains about the code underneath the loop and flags it with Unreachable Statements.
Your second case is pretty straightforward and this answer is already too long... ;)
CORRECTION:
After another analysis I believe that I was wrong to say that a null value can be boxed/autoboxed to anything. Talking about the class Integer, explicit boxing consists in invoking the new Integer(...) constructor or maybe the Integer.valueOf(int i); (I found this version somewhere). The former would throw a NumberFormatException (and this does not happen) while the second would just not make sense since an int cannot be null...
Actually, in the first case the expression can be evaluated, since the compiler knows, that it must be evaluated as an Integer, however in the second case the type of the return value (null) can not be determined, so it can not be compiled. If you cast it to Integer, the code will compile.
private int temp() {
if (true) {
Integer x = null;
return x;// since that is fine because of unboxing then the returned value could be null
//in other words I can say x could be null or new Integer(intValue) or a intValue
}
return (true ? null : 0); //this will be prefectly legal null would be refrence to Integer. The concept is one the returned
//value can be Integer
// then null is accepted to be a variable (-refrence variable-) of Integer
}
How about this:
public class ConditionalExpressionType {
public static void main(String[] args) {
String s = "";
s += (true ? 1 : "") instanceof Integer;
System.out.println(s);
String t = "";
t += (!true ? 1 : "") instanceof String;
System.out.println(t);
}
}
The output is true, true.
Eclipse color codes the 1 in the conditional expression as autoboxed.
My guess is the compiler is seeing the return type of the expression as Object.
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.