Why is the ArrayStoreException a RuntimeException? - java

Let's say we have the following program:
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
public class Main {
public static void main(String[] args) {
Fruit[] fruit = new Apple[10];
try {
fruit[0] = new Fruit(); // ArrayStoreException
fruit[0] = new Orange(); // ArrayStoreException
} catch(Exception e) { System.out.println(e); }
}
}
Based on the Java documentation:
Thrown to indicate that an attempt has been made to store the wrong
type of object into an array of objects.
I've read here that
When array is created it remembers what type of data it is meant to store.
If the array remembers what type of data it contains, it means that it KNEW the type of data it contains. But the snippet I posted is correctly compiled, so at compile time the array apparently doesn't know what type contains.
My questions are:
Why is the ArrayStoreException thrown only at runtime?
What information are missing to the compiler to realise that that assignment is not possible?
Is there any cases in which such code is correct so no ArrayStoreException is thrown?

If the array remembers what type of data it contains, it means that it KNEW the type of data it contains.
At execution time, yes... just like at execution time, the type of an object is known:
Object x = "foo";
// The compiler won't let you call x.length() here, because the variable
// x is of type Object, not String.
The alternative would be to make very many array assignments implicitly throw a checked exception. That would be awful - similar to making NullPointerException checked.
What information are missing to the compiler to realise that that assignment is not possible?
Arrays are covariant, as you've seen. When the compiler sees an assignment into a Fruit[] of an Apple, it can't know what the actual type of that array is going to be. If it's Fruit[] or Apple[], that's fine. If it's Orange[] it's not. That information is only present at execution time - again, just like the compiler doesn't know whether an expression is definitely not null.
Is there any cases in which such code is correct so no ArrayStoreException is thrown?
Well if you have an array with a compile-time element of a final class, then you can't have any lower variance. So for example:
public void foo(String[] array) {
array[0] = "x";
}
That can throw exceptions due to array being null or empty, but it will never throw an ArrayStoreException, because String is final. The implementation could never be a SubclassOfString[].

It's a runtime exception for the same reason ClassCastException is. It's not always possible to tell at compile time whether the type is what you expect or not.
Consider this example:
void method1() {
Fruit[] fruits = getFruits();
fruits[0] = new Orange();
}
Fruit[] getFruits() {
if (someCondition) {
return new Apple[5];
} else {
return new Orange[5];
}
}
There's no way for the compiler to know what state someCondition will be in when you call getFruits(). Hence the runtime exception.

When array is created it remembers what type of data it is meant to
store.
The array "remembers" only what type it actually contains during runtime.
First the array is declared, in this case as an array of Fruit.
Then the array is created, in this case as an array of Apple.
Creation is made during runtime, but the compiler is designed only to verify that the array only is assigned objects of the type it is declared as. There are so many things that can occur during runtime.
Consider the following code:
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
public class Main {
public static void main(String[] args) {
Fruit[] fruit = new Apple[10];
boolean alt = (Math.random() < 0.5);
try {
fruit[0] = fruitFactory(alt);
} catch(Exception e) { System.out.println(e); }
}
private static Fruit fruitFactory(boolean apple) {
if (apple) {
return new Apple();
} else {
return new Orange();
}
}
}
The code is identical to your except that the fruit[0] is assigned a value by the fruitFactory method. There is no way the compiler can tell if the boolean alt is going to be true or false.
What information are missing to the compiler to realise that that
assignment is not possible?
As said - the compiler cannot tell if the assignment is possible or not.
Is there any cases in which such code is correct so no
ArrayStoreException is thrown?
Yes, in 50 % of the cases in the code above. You either have to verify that the object assigned is the same as the array or catch the exception.

At your case, apple and orange are implicitly casted into fruit, because they are subclass of fruit. That's why it"s not throwing an exception, and this behavior is one of the OOP basics : it's called polymorphism.
if the array was declared as an apple array and you try to add fruit inside(the opposite of your case), then an exception will be thrown : because you can implicitly cast only from child to parent(the cast from parent to child should be explicit).

Related

Why is usage of instanceof is not allowed, but Class.isInstance() is allowed in Generics?

I was reading about Generics from ThinkingInJava and found this code snippet
public class Erased<T> {
private final int SIZE = 100;
public void f(Object arg) {
if(arg instanceof T) {} // Error
T var = new T(); // Error
T[] array = new T[SIZE]; // Error
T[] array = (T)new Object[SIZE]; // Unchecked warning
}
}
I understand the concept of erasure and I know that at runtime, there is no type for T and it is actually considered an Object (or whatever the upper bound was)
However, why is it that this piece of code works
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind) {
this.kind = kind;
}
public boolean f(Object arg) {
return kind.isInstance(arg);
}
}
Shouldn't we apply the same argument here as well that because of erasure we don't know the type of T at runtime so we can't write anything like this? Or am I missing something here?
In your example, T is indeed erased. But as you pass kind, which is the class object of the given type, it can be perfectly used for the said check.
Look what happens when you use this class:
ClassTypeCapture<String> capture = new ClassTypeCapture<>(String.class);
Here, the class object of String is passed to the given constructor, which creates a new object out of it.
During class erasure, the information that T is String is lost, but you still have
ClassTypeCapture capture = new ClassTypeCapture(String.class);
so this String.class is retained and known to the object.
The difference is that you do have a reference in the second snippet to an instance of java.lang.Class; you don't have that in the first.
Let's look at that first snippet: There is only one instance of Erased as a class. Unlike, say, C templates which look a bit like generics, where a fresh new class is generated for each new type you put in the generics, in java there's just the one Erased class. Therefore, all we know about T is what you see: It is a type variable. Its name is T. Its lower bound is 'java.lang.Object'. That's where it ends. There is no hidden field of type Class<T> hiding in there.
Now let's look at the second:
Sure, the same rule seems to apply at first, but within the context of where you run the kind.isInstance invocation, there's a variable on the stack: kind. This can be anything - with some fancy casting and ignoring of warnings you can make a new ClassTypeCapture<String>() instance, passing in Integer.class. This will compile and even run, and then likely result in all sorts of exceptions.
The compiler, just by doing some compile time lint-style checks, will really try to tell you that if you try to write such code that you shouldn't do that, but that's all that is happening here. As far as the JVM is concerned, the String in new ClassTypeCapture<String>(Integer.class) and the Integer are not related at all, except for that one compile-time check that says: The generics aren't matching up here, so I shall generate an error. Here is an example of breaking it:
ClassTypeCapture /* raw */ a = new ClassTypeCapture<Integer>(String.class);
ClassTypeCapture<Integer> b = a;
this runs, and compiles. And b's (which is the same as a's - same reference) 'kind' field is referencing String.class. The behaviour of this object's f() method is very strange.
we don't know the type of T at runtime
You're missing the point of generics: generics allow the compiler to "sanity check" the types, to make sure they're consistent.
It's tempting to read ClassTypeCapture<T> as "a ClassTypeCapture for type T", but it's not: it's a ClassTypeCapture, with a hint to the compiler to check that all of the method invocations/field accesses/return values involving that reference are consistent with the type T.
To make this more concrete (let's do it with List, that's easier):
List<String> list = new ArrayList<>();
list.add("hello");
String e = list.get(0);
the <String> is an instruction to the compiler to do this:
List list = new ArrayList();
list.add("hello"); // Make sure this argument is a `String`
String e = (String) list.get(0); // Insert a cast here to ensure we get a String out.
At runtime, the "T-ness" isn't known any more, but the ClassTypeCapture (or Object, or String, or whatever) still is. You can ask an Object if it's an instance of an Object, String, ClassTypeCapture, or whatever.
You just can't ask it if it was a ClassTypeCapture<String> at compile time, because that <String> is just a compiler hint, not part of the type.

Weird exception "Invalid receiver type class java.lang.Object; not a subtype of ..."

I'm getting this strange exception in code run using jre1.8.0_66:
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
at main
Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Object; not a subtype of implementation type interface Fruit
at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
... 3 more
What's it mean? The code is as follows:
public static interface Fruit {
int getPickingMonth();
}
public static class Apple implements Fruit, Serializable {
#Override
public int getPickingMonth() {
return 11;
}
}
public static class Orange implements Fruit, Serializable {
#Override
public int getPickingMonth() {
return 2;
}
}
public static void main(String[] args) {
List<Apple> apples = Arrays.asList(new Apple());
List<Orange> oranges = Arrays.asList(new Orange());
Stream.of(apples.stream(), oranges.stream())
.flatMap(Function.identity())
.map(Fruit::getPickingMonth) // exception occurs on this line
.forEachOrdered(System.out::println);
}
The exception goes away if I change Fruit::getPickingMonth to x -> x.getPickingMonth().
For what it's worth: The exception also goes away if I remove Serializable from either class. But returns if I add another, equivalent interface to both classes, e.g. Cloneable or some custom interface.
You ran into the same compiler bug that has been discussed in this question and that question.
The problem occurs whenever an intersection type is involved and you are using a method reference using a receiver type other than the first one (the first type is the one that will remain after type erasure).
So when you replace the method reference with a lambda expression, you are not affected by the bug anymore. If you remove the Serializable from the types instead, the inferred element type of the Stream will be Fruit, i.e. not an intersection type, and again the problem does not occur. But with the two element types implementing Fruit and Serializable, the compiler will infer the element type Object&Fruit&Serializable and the raw type will be Object which provokes the error when using a method reference with the receiver type Fruit. You can easily work around this:
Stream.of(apples.stream(), oranges.stream())
.<Fruit>flatMap(Function.identity())
.map(Fruit::getPickingMonth) // no more exception on this line
.forEachOrdered(System.out::println);
The compiled code will be identical to your original, but the formal result type of the flatMap operation will be Stream<Fruit>, ignoring all other artifacts of the inferred intersection type. As a consequence the method reference Fruit::getPickingMonth will implement the type Function<Fruit,Integer> instead of Function<Object&Fruit&Serializable,Integer> and the compiler bug does not materialize.
But note that your code is unnecessarily complicated. You can simply use
Stream.<Fruit>concat(apples.stream(), oranges.stream())
.map(Fruit::getPickingMonth) // no more exception on this line
.forEachOrdered(System.out::println);
to achieve the same.
I believe you are just trying to reference a method from the interface with no return.
ie:
Fruit::getPickingMonth; //cant return anything
I would imagine you would want something like
Apple::getPickingMonth;
or
Orange::getPickingMonth;
Instead
If the above isn't the solution it might be a casting issue where the compiler doesn't know what to return on the bytecode level.
There are questions like this on StackOverflow
Lambda Referencing
Lambda Conversion Exception

Why does the JVM allow be to pass a B[] to a method that expects an A[]?

I have the following generic test class:
public class BrokenGenerics<T> {
private T[] genericTypeArray;
public BrokenGenerics(T... initArray) {
genericTypeArray = initArray;
}
public void setArray(T[] newArray) {
genericTypeArray = newArray;
}
public T get(int idx) {
return genericTypeArray[idx];
}
public Class getType() {
return genericTypeArray.getClass().getComponentType();
}
public static boolean breakThis(BrokenGenerics any) {
any.setArray(new B[]{new B(2)});
return false;
}
public static void main(String[] args) {
BrokenGenerics<A> aBreaker = new BrokenGenerics<A>(new A("1"));
System.out.println(aBreaker.get(0));
System.out.println(aBreaker.getType());
breakThis(aBreaker);
System.out.println(aBreaker.get(0));
System.out.println(aBreaker.getType());
}
private static class A {
public String val;
public A(String init) {
val = init;
}
#Override
public String toString() {
return "A value: " + val;
}
}
private static class B {
public int val;
public B(int init) {
val = init;
}
#Override
public String toString() {
return "B value: " + val;
}
}
}
When I run it, I get this output, and no errors:
A value: 1
class BrokenGenerics$A
B value: 2
class BrokenGenerics$B
Now, I understand why this compiles; it can't know at compile-time that breakThis is being passed a generic of a bad type. However, once it runs the line any.setArray(new B[]{new B(2)});, shouldn't it throw a ClassCastException (NOTE THAT IT DOES NOT! Try it yourself!) because I'm trying to pass a B[] to a method that expects an A[]? And after that, why does it allow me to get() back the B?
After Type Erasure, T will be turned into Object since you didn't specify a bound on T. So, there is no problem at runtime assigning any type of array to genericTypeArray, which is now of type Object[] or calling the function setArray(...), which now also accepts an argument of type Object[]. Also, your get(...) method will simply return an Object.
Trouble starts when you access elements in the array with a wrong type expectation, since this might lead to (implicit or explicit) illegal type casts, for example by assigning the value returned by get(...) to a variable of type A.
You can also get a run-time ClassCastException if you try to type-cast the array itself, but, in my experience, that is a case that tends to come up less often, although it can be very obscure to find or even understand if it does happen. You can find some examples below.
All generics-checking happens only at compile-time. And if you use raw types, these checks can not be performed rigorously, and thus the best the compiler can do is to issue a warning to let you know that you are giving up an opportunity for more meaningful checks by omitting the type argument.
Eclipse with its standard settings (and probably the java compiler with the correct flags) shows these warnings for your code:
"Class is a raw type" where you define getType() (somewhat unrelated to your question)
"BrokenGenerics is a raw type" where you define breakThis(...)
"Type safety: The method setArray(Object[]) belongs to the raw type
BrokenGenerics" where you call setArray(...) inside breakThis(...).
Examples for causing ClassCastException due to illegal type-cast of the array:
You can get ClassCastExceptions at runtime if you expose the array to the outside world (which can often be a dangerous thing to do, so I try to avoid it) by adding the following to BrokenGenerics<T>:
public T[] getArray() {
return genericTypeArray;
}
If you then change your main method to:
BrokenGenerics<A> aBreaker = new BrokenGenerics<A>(new A("1"));
A[] array = aBreaker.getArray();
System.out.println(array[0]);
System.out.println(aBreaker.getType());
breakThis(aBreaker);
array = aBreaker.getArray(); // ClassCastException here!
System.out.println(array[0]);
System.out.println(aBreaker.getType());
You get the ClassCastException at runtime at the indicated position due to a cast of the array itself rather than one of its elements.
The same thing can also happen if you set the variable genericTypeArray to protected and use it from code that subclasses your generic class with a fixed type argument:
private static class C extends BrokenGenerics<A> {
public C(A... initArray) {
super(initArray);
}
public void printFirst() {
A[] result = genericTypeArray; // ClassCastException here!
System.out.println(result[0]);
}
}
To trigger the exception, add the following to you main method:
C cBreaker = new C(new A("1"));
cBreaker.printFirst();
breakThis(cBreaker);
cBreaker.printFirst();
Imagine this case coming up in a bigger project... How on earth would you even begin to understand how that line of code could possible fail?!? :) Especially since the stack trace might be of very little help trying to find the breakThis(...) call that is actually responsible for the error.
For more in-depth example cases, you can take a look at some tests I did a little while back.
shouldn't it throw a ClassCastException because I'm trying to pass a B[] to a method that expects an A[]?
No. As this post explains, your invocation of setArray in
public static boolean breakThis(BrokenGenerics any) {
any.setArray(new B[]{new B(2)});
return false;
}
is done on a reference expression of the raw type BrokenGenerics. When interacting with raw types, all corresponding generic parameters are erased. So setArray is actually expecting a Object[]. A B[] is a Object[].
why does it allow me to get() back the B?
Assuming you're asking about this
System.out.println(aBreaker.get(0));
PrintStream#println(Object) expects an Object, not an A. As such, there is no reason for the compiler to insert a cast here. Since there is no cast, there is no ClassCastException.
If you had instead done
A a = aBreaker.get(0);
or had a method like
void println(A a) {}
...
println(aBreaker.get(0));
then these would cause ClassCastException. In other words, the compiler will insert a cast (checkcast) anywhere a type needs to be converted from a generic type parameter. That was not the case with PrintStream#println.
Similarly,
System.out.println(aBreaker.getType());
doesn't even involve the generic parameter declared in BrokenGenerics
public Class getType() {...}
and also returns a value of the raw type Class. The compiler has no reason to add a checkcast to A.

What is the class type of a superclass ref pointing to a subclass object?

I have the following codes:
1. public class Tester
2. {
3. public static void main(String[] args)
4. {
5. A a = new B();
6. System.out.println(a.getClass()); //Prints class B
7. System.out.println(a instanceof A); //Prints true
8. System.out.println(a instanceof B); //Prints true
9. System.out.println(a.valA); //Prints 1
10. System.out.println(a.valB); //Compilation error
11.
12. }
13. }
14.
15. class A
16. {
17. int valA=1;
18. }
19.
20. class B extends A
21. {
22. int valB=2;
23. }
At line 6 it shows that a is of type class B. However when it reaches line 10, the compiler produces an error: location: variable a of type A.
So my question is: What exactly is the class type of a now? Why getClass() shows that it is of type class B, yet the compiler complains it as type A during compilation?
Further more, since a instanceof B is true, why can't I access valB?
To make things clearer:
EDIT: I ran this statement: System.out.println(a); and the output was B#36d98810 which somehow proves that the toString() method of class B was executed. Since variable a can access the toString() method within class B, why can't it access valB which also resides in class B?
Professor Jonathan Shewchuk from UC Berkley explains about shadowing over here. Start at 18 minutes. (If the link changes just google search for CS 61B Lecture 15: More Java)
To answer your question in short there are two types for a variable, static type and dynamic type.
Static type is its Type at compile time
Dynamic type is its Type at run time.
In your example
A a = new B();
The static type of a is A and the dynamic type of a is B.
In Java a variable gets its non static methods from dynamic type
(if the method exists in both the parent and child class)
and
its fields and static methods from the static type.
This is true in C# only if the method is overridden in the sub class
Update:
The line
a instanceof A
tells you whether the dynamic type of a is of type A OR a subclass of A
Update 2:
AN example that illustrates this
public class PlayGround {
public static void main(String[] args) {
Animal a = new Dog();
System.out.print(a.name);// displays animal
System.out.print("\r\n");
a.MakeStaticSound();// displays static animal sound
System.out.print("\r\n");
a.MakeSound();// displays bow wow
}
}
class Animal {
public String name = "animal";
public void MakeSound() {
System.out.print("animal sound");
}
public static void MakeStaticSound() {
System.out.print("static animal sound");
}
}
class Dog extends Animal {
public String name = "dog";
public void MakeSound() {
System.out.print("bow wow");
}
public static void MakeStaticSound() {
System.out.print("static bow wow");
}
}
Please note that the more readable and preferred way to call a.MakeStaticSound() is Animal.MakeStaticSound()
a is not an object. It's a variable.
The type of the variable is A. The type of the object that the value of the variable refers to at execution time is B.
The compiler resolves everything against the compile-time type of the expressions involved - the variable in this case. When trying to resolve the name valB within the compile-time type of A, it fails to find anything - hence the error.
You need to keep in mind that compilation and execution are two different processes that happen at different times and have different kinds of information available to them. The compiler has to predict the future -- it has to decide whether it can guarantee that your code will make sense in the future, at runtime. It does this by analyzing the types of the objects in your code. The runtime, on the other hand, just has to inspect the current state of things.
When you read the line A a = new B(), you are inferring more information about the a local variable than the compiler is. The compiler basically just sees this as A a = <some expression>. It does not take note of the contents of the expression that's used to produce the value for a.
The fact that you've said A a = ... is you telling the compiler: "hey, this a thing I'm going to deal with in the rest of my program, it's just an A, don't assume anything more about it." If you had instead said B a = ..., then you're telling the compiler that it's a B (and the compiler also sees B extends A elsewhere in your code, so it knows it's also an A).
The subsequent expressions a instanceof A, a instanceof B, a.getClass(), and a.toString() are legal, from the compiler's point of view, regardless of the type of a: the instanceof operator and the getClass() and toString() methods are defined for all Objects. (The compiler does not need to predict what value those expressions will produce at runtime, just that they will produce either true or false, some Class<?>, and some String, respectively.)
But then when you come to a.valA and a.valB, the compiler actually has to do some real work. It needs to prove or guarantee that the a object will have a valA and a valB field at runtime. But since you've explicitly told it earlier to just assume that a is an A, it can not prove that it will have a valB field at runtime.
Now, later on, at execution time, the JVM has more information. When it evaluates a.getClass(), it actually looks up the concrete class that's "under the hood" of a and returns it. Similarly for instanceof B -- it looks up the concrete class and thus the result of that expression is true.
a.toString() works similarly. At runtime, the JVM knows that the thing referenced by a is actually a B, so it executes B's toString method.
This is a fundamental property of class inheritance, interfaces, etc.
Class "A" does not have a variable "valB".
If you want to use the variable "valB" in class "B" either, you should first cast Class "A" to "B"
Try :
System.out.println(((B)a).valB);
You should know the difference between object type and instance type. First is determined at compile type and at runtime it's doing the best to keep that type safe. Instance type is a class which object is instantiated.
A a; //this is an object type
new B(); //this is an instance type
A a = new B(); //all together, but a is of type A, having instance of type B.

can I use isAssignableFrom to check C1 is a subtype of C2 in Java?

Here is an example:
class test {
static interface I<A> { }
static class Y implements I<Integer> { }
public static void main(String[] args) {
// this is not allowed by the compiler:
//I<String> s = new Y() ;
// yet:
System.out.println(">" + I.class.isAssignableFrom(Y.class)) ; // true!
}
}
So, I conclude that isAssignable is not always correct in its answer. Is this a correct conclusion? Is there a better way to check for subtyping at the runtime?
Thanks. --Wish.
You got tripped up by type erasure. The isAssignableFrom method operates on the erased (raw) type I. The compiler, on the other hand sees you try to assign a I<Integer> to an I<String>, and disallows the assignment.
You may be able to determine the actual implemented type via getGenericInterfaces et. al. Then you can check for the returned type being an instance of ParamterizedType, and get the actual type bound.
Usually, this is very rarely necessary, though.

Categories