question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

ClassCastExceptions with JDK9 javac

See original GitHub issue

JDK 9 fixes a javac bug (JDK-8058199) that was causing checkcast intructions to be skipped. Previously javac used the parameter types of a method symbol’s erased type as targets when translating the arguments. In JDK 9, javac has been fixed to use the inferred types as targets. The fix causes additional checkcasts to be generated if the inferred types do not have the same erasure.

The fix breaks Mockito answer strategies that pick types based on the erased method signature’s return type, and causes tests to fail with ClassCastExceptions when compiled with the JDK 9 javac.


Example 1

class Super<T> {
  T g() {
    return null;
  }
}

class Sub extends Super<Boolean> {}
@Mock Sub s;
when(s.g()).thenReturn(false);

compiled with javac 8

INVOKEVIRTUAL Sub.g ()Ljava/lang/Object;
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;

compiled with javac 9

INVOKEVIRTUAL Sub.g ()Ljava/lang/Object;
CHECKCAST java/lang/Boolean
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;

The erased return type of Super.g is Object, but the expected return type of Sub.g is Boolean. If the answer strategy returns Object the checkcast fails.


Example 2

class Foo {
  <T> T getFirst(Iterable<T> xs) { return xs.iterator().next(); }
}
@Mock Foo f;
Iterable<Boolean> it = Arrays.asList(false);
when(f.getFirst(it)).thenReturn(false)

compiled with javac 8

INVOKEVIRTUAL Foo.getFirst (Ljava/lang/Iterable;)Ljava/lang/Object;
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;

compiled with javac 9

INVOKEVIRTUAL Foo.getFirst (Ljava/lang/Iterable;)Ljava/lang/Object;
CHECKCAST java/lang/Boolean
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;

The erased return type of Foo.getFirst is Object, but the inferred return type of getFirst(Iterable<Boolean>) is Boolean. If the answer strategy returns Object the checkcast fails.


The first example could be fixed by using GenericMetadataSupport in all of the answer implementations instead of invocation.getMethod().getReturnType().

It gets more difficult if the mock’s type is an instantiation of a generic type (e.g. @Mock Foo<Bar> x;), since the field’s type arguments get dropped. I think fixing that would require adding support for mocking types, not just classes.

For the second example, returning the right answer requires considering the generic signature of the invoked method, and performing type inference using the argument types. Unfortunately the runtime type of the argument is going to be a raw Iterable and the inference depends on knowing it’s Iterable<Boolean>, so I’m not sure what to do there.

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Reactions:2
  • Comments:23 (22 by maintainers)

github_iconTop GitHub Comments

2reactions
raphwcommented, Oct 3, 2017

Only when returning automatic mocks from mocks with generic return types, maybe we should add a warning for this to make the error more intuitive.

0reactions
cushoncommented, Oct 12, 2017

There’s an Error Prone check that detects this problem at compile-time: http://errorprone.info/bugpattern/MockitoCast. It can also be run as a refactoring to add work-arounds to affected code.

Read more comments on GitHub >

github_iconTop Results From Across the Web

ClassCastException (Java SE 9 & JDK 9 ) - Oracle Help Center
Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance. For...
Read more >
Why am I getting a ClassCastException when generating ...
It looks like this has been reported as a Java bug. It appears to be caused by using annotations from a 3rd party...
Read more >
Explanation of ClassCastException in Java - Baeldung
ClassCastException is an unchecked exception that signals the code has attempted to cast a reference to a type of which it's not a...
Read more >
JDK-8201132 ClassCastException during Javadoc generation
FULL PRODUCT VERSION : openjdk version "9.0.4" OpenJDK Runtime Environment (build 9.0.4+11) OpenJDK 64-Bit Server VM (build 9.0.4+11, mixed mode) ADDITIONAL ...
Read more >
Regression: ClassCastException: Type$ErrorType cannot be ...
It just prints error as expected when run on JDK10b24 and on earlier builds (including JDK9) === full source attached to the bug...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found