I am having a stump of a time understanding the condundrum below. Here is a code snippet that compiles, yet throws the exception
Exception in thread "main" java.lang.ClassCastException:
TestGenericSingleton$$Lambda$1/303563356 cannot be cast to
TestGenericSingleton$IntegerConsumer
at TestGenericSingleton.main(TestGenericSingleton.java:23)
import java.util.function.Consumer;
public class TestGenericSingleton
{
static final Consumer<Object> NOOP_SINGLETON = t -> {System.out.println("NOOP Consumer accepting " + t);};
#SuppressWarnings("unchecked")
static <R extends Consumer<T>, T> R noopConsumer()
{
return (R)NOOP_SINGLETON;
}
static interface IntegerConsumer extends Consumer<Integer> {};
public static void main(String[] argv)
{
Consumer<Boolean> cb = noopConsumer();
cb.accept(true);
IntegerConsumer ic = t -> {System.out.println("NOOP Consumer2 accepting " + t);} ;
ic.accept(3);
ic = noopConsumer();
ic.accept(3);
System.out.println("Done");
}
}
What stumps me is that the Java compiler can generate a proper IntegerConsumer-compatible object out of the lambda on line 20, yet the previously constructed non-generic lambda constructed as the singleton on line 8 can not be used. Is that because the lambda on line 20 has a reifiable subtype of the Consumer that fits the type of the IntegerConsumer reference immediately, whereas the lambda that is cast on line 10 can not be cast at runtime to a real subtype of Consumer ? But then shouldn't the generic bounded type declaration on line 8 take care of that? Any help is really appreciated !
the previously constructed non-generic lambda constructed as the singleton on line 8 can not be used
We will remove lambda and understand the root cause of the exception. Let's consider the below simpler example.
public class TestGenericObject {
static final Object NOOP_SINGLETON = new Object();
static <R extends Object> R noopConsumer() {
return (R) NOOP_SINGLETON;
}
public static void main(String[] argv) {
Object cb = noopConsumer();
Integer ic = noopConsumer(); // Throws java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Integer
}
}
The exception is justified. NOOP_SINGLETON's actual type is Object but we are trying to cast it to Integer. This would be same as trying, Integer ic = (Integer) new Object(). In your case, for the same reason you cannot cast Consumer<Object> type to IntegerConsumer.
One interesting observation is the exception is not thrown within the noopConsumer() and is rather thrown in the main().
Below is the output of javap -v -c for the method noopConsumer
... // Removed lines for clarity
static <R extends java.lang.Object> R noopConsumer();
...
Code:
stack=1, locals=0, args_size=0
0: getstatic #2 // Field NOOP_SINGLETON:Ljava/lang/Object;
3: areturn
You can see there is no op code present for casting. But for the main()
public static void main(java.lang.String[]);
Code:
stack=1, locals=3, args_size=1
0: invokestatic #3 // Method noopConsumer:()Ljava/lang/Object;
3: astore_1
4: invokestatic #3 // Method noopConsumer:()Ljava/lang/Object;
7: checkcast #4 // class java/lang/Integer
10: astore_2
11: return
at this line 7:checkcast #4, it checks if returned type is of Integer. This behaviour is due to 2 reasons
In noopConsumer(), the tighter bound of R is Object and NOOP_SINGLETON is of type Object as well. Hence, post type erasure, the casting is redundant and removed.
The reason main() has a cast check is again due to Type Erasure. As mentioned in the link, if required, type casting will be inserted during type erasure.
Back to Lambdas. Lambdas use invokedynamic opcode to generate code at runtime. This and this are excellent resources to understand better about lambda handling at runtime.
For the below code,
public static void main(String[] argv) {
Consumer<Object> NOOP_SINGLETON = t -> {System.out.println("NOOP Consumer accepting " + t);};
TestGenericSingleton.IntegerConsumer ic = t -> {System.out.println("NOOP Consumer2 accepting " + t);} ;
}
lets analyse the byte code.
public static void main(java.lang.String[]);
...
Code:
stack=1, locals=3, args_size=1
0: invokedynamic #2, 0 // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;
5: astore_1
6: invokedynamic #3, 0 // InvokeDynamic #1:accept:()Lcom/TestGenericSingleton$IntegerConsumer;
11: astore_2
12: return
The invokedynamic passes 2 different expected types Ljava/util/function/Consumer and ()Lcom/TestGenericSingleton$IntegerConsumer to LambdaMetafactory.metafactory().
So though the code t -> {System.out.println("NOOP Consumer accepting " + t);} within the lambdas are same, they are 2 different types.
To summarize, the lambdas are built at runtime and the returned instance will have the type specified in the declaration. Hence, NOOP_SINGLETON is of type Consumer and ic is of type IntegerConsumer. Casting from type Consumer to IntegerConsumer will fail for the same reason, Integer ic = (Integer)new Object() fails.
Related
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.
Is there a reason that a Java Generic Method cannot be called without the Static/Instance reference before the method? Like "case 2" and "case 5" on the example code.
In other words, why can we call a normal method without the static/instance reference (like in "case 3") and in Generic Methods we can't?
public class MyClass {
public static void main(String[] args) {
MyClass.<String>doWhatEver("Test Me!"); // case 1
<String>doWhatEver("Test Me2!"); // case 2 COMPILE ERROR HERE
doSomething("Test Me 3!"); // case 3 (just for compare)
new MyClass().<String>doMoreStuff("Test me 4"); // case 4
}
public void doX(){
<String>doMoreStuff("test me 5"); // case 5 COMPILE ERROR HERE
}
public static <T> void doWhatEver(T x){
System.out.println(x);
}
public static void doSomething(String x){
System.out.println(x);
}
public <T> void doMoreStuff(T x){
System.out.println(x);
}
}
You don't need to specify <String> for case 1 and 4, the compiler will handle this for you.
Now let's try to run your exemple and see what happen.
Exception in thread "main" java.lang.RuntimeException: Uncompilable
source code - illegal start of expression
It's that much simple, the answer to your question is because the syntax is invalid, it was not meant to be used that way in the javac specifications.
However, this has nothing to do with being static or not. Try it in a constructor removing the static keyword to doWhatEver method :
public MyClass()
{
<String>doWhatEver("Test Me2!"); //does not compile
doWhatEver("Test Me2!"); //compile
}
public <T> void doWhatEver(T x){
System.out.println(x);
}
Now if you are wondering why MyClass.<String>doWhat.. compiled while <String>doWhat.. did not compile even if we modify the static keyword, let's have a look at the generated bytecode.
Your line will be compiled to this :
6: invokestatic #5 // Method doWhatEver:(Ljava/lang/Object;)V
Which correct the syntax error you made, but why ?.
Try compiling for example these two lines
MyClass.<String>doWhatEver("Test Me2!");
MyClass.doWhatEver("Test Me3!");
then run javap -v on the .class file and you will notice that the both call was compiled to the same bytecode.
4: ldc #4 // String Test Me2!
6: invokestatic #5 // Method doWhatEver:(Ljava/lang/Object;)V
9: ldc #6 // String Test Me3!
11: invokestatic #5 // Method doWhatEver:(Ljava/lang/Object;)V
In the case were you call the non-static method, the generated bytecode will be invokevirtual instead :
17: invokevirtual #8 // Method doWhatEver2:(Ljava/lang/Object;)V
My guess is that invokestatic will search directly in the constant pool (where static method are stored) for the method corresponding to the specified call and will ommit the type declaration, while invokevirtual will search in the actual class.
This question already has answers here:
Use of uninitialized final field - with/without 'this.' qualifier
(4 answers)
Closed 8 years ago.
I wrote this piece of code and it seems compiler allows accessing uninitialized blank final field when accessed using 'this' keyword:
public class TestClass
{
public final int value1;
public int value2;
TestClass(int value) {
value2 = 2 + this.value1; // access final field using 'this' before initialization gives no compiler error
//value2 = 2 + value1; // uncomment it gives compile time error - variable value1 might not have been initialized
value1 = value;
}
public static void main(String args[]) {
TestClass tc = new TestClass(10);
System.out.println("\nTestClass Values : value1 = " + tc.value1 + " , value2 = " + tc.value2);
}
}
I tried compiling it on 1.5, 1.6, & 1.7 and got same result in all three of them.
To me it looks like compiler bug because compiler must throw error in this case but with 'this' keyword it doesn't and thus creates scope of coding error as it will go unnoticed by the programmer since no compile-time or run-time error will be thrown.
FEW POINTS WHY IT IS NOT A DUPLICATE
- all answers are explaining how it works and what JLS says, fine, but my real intent here is should that be allowed at the first place?
- my question here is more from programmer's point of view and not language semantics
This is not a bug. This a feature for Java Specification 1.6 and lower.
The final field can be accessed at any part of the code. There is no restriction about it.
The only restriction in case of final is that it has to be initialized before class instance is created.
When you use this, you express that it is element of the object that is being constructed.
Since the specification 1.7 because of change we may say that this is bug in some implementation of compilers.
But since 1.8 the code will produce same error for this.value1 or value1.
In Java ints have a default value of 0, so even if you did not initialize it, the compiler has still assigned 0 as value.
Fields that are declared but not initialized will be set to a reasonable default by the compiler. Generally speaking, this default will be zero or null, depending on the data type.
Source: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
Note that this does not apply to local variables, only field variables have a default value.
I used the following code
public class HelloWorld {
public final int value1;
public int value2;
public HelloWorld(int value){
System.out.println(this.value1);
System.out.println(this.value2);
value1 = value;
}
public static void main(String args[]) {
}
}
and the bytecode it generates is
Compiled from "HelloWorld.java"
public class test.HelloWorld extends java.lang.Object{
public final int value1;
public int value2;
public test.HelloWorld(int);
Code:
0: aload_0
1: invokespecial #11; //Method java/lang/Object."<init>":()V
4: getstatic #14; //Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_0
8: getfield #20; //Field value1:I
11: invokevirtual #22; //Method java/io/PrintStream.println:(I)V
14: aload_0
15: iload_1
16: putfield #20; //Field value1:I
19: return
public static void main(java.lang.String[]);
Code:
0: return
}
If you notice JVM does a getstatic call in case of final and getfield in case of normal instance variable. Also now if you look at the specs for getstatic it says
On successful resolution of the field, the class or interface that declared the resolved field is initialized (§5.5) if that class or interface has not already been initialized.
So if you use this with a final variable then it is initialize it. Not it will initialize this.value1 and not value1 which is final. You still have to initialize it before using.
In my code, it appears convenient to use varargs when implementing a generic method when the type is an array:
public interface Codec<D,E> {
E encode(D decoded);
D decode(E encoded);
}
public class MyCodec implements Codec<byte[], char[]> {
#Override char[] encode(byte... decoded) {...}
#Override byte[] decode(char... encoded) {...}
}
When I write this, Eclipse shows a warning:
Varargs methods should only override or be overridden by other varargs
methods unlike MyCodec.encode(byte...) and
Codec.encode(byte[])
Should I just ignore the warning, or is this going to cause some unforeseen problems?
This is an Eclipse-specific warning. It has nothing to do with generics specifically and can be reproduced with this example:
class A {
m(int[] ints) { }
}
class B extends A {
#Override
m(int... ints) { }
}
As the other answers point out, varargs are purely a compile-time feature and there's no difference at runtime. I tried searching for the specific reasoning behind the warning but couldn't turn anything up. Likely it's considered bad practice to alternate method overrides between varargs and non-varargs because it's confusing and arbitrary. But this is in general - your use case seems more reasonable as long as callers are always going to be using a statically-typed MyCodec instead of coding to interface with a Codec<byte[], char[]>.
Unfortunately there is no way to suppress this warning - even #SuppressWarnings("all") won't make it yield. Which is unfortunate considering how obscure of a warning it is. Here's an ancient conversation about this same issue: http://echelog.com/logs/browse/eclipse/1196982000 (scroll to 20:45:02) - proving it's bit people long before you. Seems like an Eclipse bug that it can't be suppressed.
I wrote two test files. Here's the first:
public class Test {
public static void main(String... args) {
System.out.println(java.util.Arrays.toString(args));
}
}
And here's the second:
public class Test {
public static void main(String[] args) {
System.out.println(java.util.Arrays.toString(args));
}
}
(The only difference between these two files is the String[] args vs String... args.)
Then, I ran javap -c on each file to see the disassembly. The contents of the main method were identical:
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokestatic #3 // Method java/util/Arrays.toString:([Ljava/lang/Object;)Ljava/lang/String;
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
The only difference was the method header, which was simply the method signature of each method:
public static void main(java.lang.String[]);
public static void main(java.lang.String...);
With this in mind, I would say that it's a safe assumption that nothing bad will happen.
According to the bytecode, no problem.
public byte[] encode(char...);
flags: ACC_PUBLIC, ACC_VARARGS
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LMyEncoder;
0 2 1 args [C // char[]
Code:
stack=1, locals=2, args_size=2
0: aconst_null
1: areturn // return null;
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LMyEncoder;
0 2 1 args [C
public java.lang.Object encode(java.lang.Object);
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_VARARGS, ACC_SYNTHETIC
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: checkcast #27 // class "[C" -> char[]
5: invokevirtual #28 // Method encode:([C)[B -> return byte[]
8: areturn
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
If you make a call using one reference of the interface, the method with the flag ACC_BRIDGE check (checkcast) if the type of the argument is the same as is defined in the type parameter (java.lang.ClassCastException otherwise, but will never happen if you allways provides the type parameter), then run the method implementation.
In another hand, if you compile this with javac, none warning is show.
This question has to do with type erasure and its implication on arrays SCJP (by Mughal - 3rd edition on page 726). Here's the code in short:
public class MyStack<E> implements IStack<E> {
// Top of stack.
private Node<E> tos;
// Size of stack
private int numOfElements;
:
// Incorrect version
public E[] toArray3() {
E[] toArray = (E[])new Object[numOfElements];
int i=0;
for (E data : this) {
toArray[i++] = data;
}
return toArray;
}
// Correct version
public E[] toArray4(E[] toArray) {
if (toArray.length != numOfElements) {
toArray = E[])java.lang.reflect.Array.newInstance(toArray.getClass().getComponentType(), numOfElements);
}
int i=0;
for (E data : this) {
toArray[i++] = data;
}
return toArray;
}
}
According to the book, method toArray3 is the incorrect version to use while method toArray4 is the correct version since array is of reifiable type. I don't have problem on this. However, I have a problem when it comes to understanding why type parameter E is different for toArray3 and toArray4.
Here's how I found out. Supposedy I have the following:
MyStack<Integer> intStack = new MyStack<Integer>();
intStack.push(9);
Integer[] x = intStack.toArray4(new Integer[0]); // clause 1
System.out.println(x.length);
Integer[] y = intStack.toArray4(new Object[0]); // clause 2 - compiler error
System.out.println(y.length);
Integer[] z = intStack.toArray3(); // clause 3
System.out.println(z.length);
a. For clause 1, no problem. The type parameter E in toArray4 seems to be of Integer.
b. For clause 2, the error is saying that Integer[] is not applicable for Object[]. Does that mean that type erasure of toArray4(E[] toArray) is toArray4[Integer[] toArray)? Why not Object[]? (Though I know that intStack has been instiantiated with Integer.)
c. For clause 3, the runtime error is that saying Object[] cannot be assigned to Integer[]. I do understand this, but why now is the type parameter E on toArray3 Object and not Integer?
I would expect the type parameter E ON toArray3 to be the same as toArray4 since type parameter E itself for intStack is of integer type which are used by both toArray3 AND toArray4.
Please bear in mind that in the body of toArray3, the array Object has been cast back to E[].
On a, no question here.
On b, type erasure happens after type validation. In compile-time, all the types are here.
On c, you explicitly create an array of Object, not Integer— no magic here.
The type parameter stays the same. It's just that you cannot cast Object[] to Integer[].
Getting away from stacks, and narrowing your problem down:
public class MyStack<E> {
public static void main(String[] args) {
Integer[] test = new MyStack<Integer>().test();
}
public E[] test() {
return (E[]) (new Object[10]);
}
}
Gives us the following bytecode:
Compiled from "MyStack.java"
public class org.acm.afilippov.stacko.MyStack extends java.lang.Object{
public org.acm.afilippov.stacko.MyStack();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class org/acm/afilippov/stacko/MyStack
3: dup
4: invokespecial #3; //Method "<init>":()V
7: invokevirtual #4; //Method test:()[Ljava/lang/Object;
10: checkcast #5; //class "[Ljava/lang/Integer;"
13: astore_1
14: return
public java.lang.Object[] test();
Code:
0: bipush 10
2: anewarray #6; //class java/lang/Object
5: checkcast #7; //class "[Ljava/lang/Object;"
8: areturn
}
Note that in the test() method, generic types are erased — it's new Object[], and checkcast Object[].
In main, you assign to a non-generic variable, which means type cast; hence checkcast #5; //class "[Ljava/lang/Integer;"
The trick which worries you in b is based on the fact that arrays are covariant, and the following is allowed:
Object[] array = new Integer[10];
So in case b the generic method has result type Object[]— but since you can assign Integer[] to Object[], it's okay. See more at Generics FAQ.
The difference between these methods is that toArray4() is using reflection to obtain the Class object that represents the correct component type of the array. This ensures that the type information is known at runtime in spite of type erasure.
The key is this line of toArray4():
Array.newInstance(toArray.getClass().getComponentType(), numOfElements);
Unlike generic objects, arrays can provide their component type, since something like Integer[] has a corresponding Class object that can be inspected for this information.
Because of this, toArray4() actually instantiates an Integer[] object when E is Integer, whereas toArray3() instantiates an Object[] and the calling code (through type erasure) attempts to cast it to Integer[], rightly causing a ClassCastException.
In general, reflection is often used as a workaround to type erasure.
As a note, arrays in Java tend to have certain "magic" associated with them, especially with regard to reflection. For example if you look at the source for Class#getComponentType() and Array.newInstance() they both lead to native code.
EDIT: I think the source of your confusion is the cast being done in toArray3():
E[] toArray = (E[])new Object[numOfElements];
This is not the cast that is causing your ClassCastException. Rather it's a second cast which is added by type erasure. To illustrate, let's rewrite toArray3() as if type erasure had been applied:
public Object[] toArray3() {
Object[] toArray = new Object[numOfElements]; //first cast no longer necessary
int i=0;
for (Object data : this) { //pretend MyStack is no longer generic
toArray[i++] = data;
}
return toArray;
}
Then in the calling code:
Integer[] z = (Integer[])intStack.toArray3(); //ClassCastException
As you can see, the ClassCastException is thrown not in the method, but when its result is assigned to z using a cast, which type erasure creates for you. We might verify this if you actually post the stacktrace.