Repository: mockito/mockito
Issue: Add back InvocationOnMock.getArgument<T>(int, Class<T>)

In https://github.com/mockito/mockito/issues/365 we replaced the existing `getArgumentAt<T>(int, Class<T>)` API with the current `getArgument(int)` API. However, when integrating this change within Google, we ran into numerous issues.

An example:

```java
invocation.getArgumentAt(0, Runnable.class).run()
```

gets rewritten to

```java
invocation.getArgument(0).run()
```

This does not compile, as the generic parameter is erasured to `Object` and thus does not have the `run` method.

Therefore, I would propose adding back an overload to `getArgument` that allows you to specify the `Class`. In general, users would not need this inside a variable assignment, but do need it in all other cases.

Issue comments:
For more information see the error-prone bugpattern: https://errorprone.info/bugpattern/TypeParameterUnusedInFormals
@mockitoguy WDYT? I think it makes sense to have a simple API for local assignments or in return statements, but we would still need the Class to be explicit.
Looking...
Good idea! Few suggestions:
 - document clearly the use case in the new method (compact, readable tests / one-liner invocations).
 - add NotExtensible annotation to InvocationOnMock

Nice catch.

Awesome, thanks for the quick response! Will open a PR today :smile: 
Instead of using the reintroduced `getArgument<T>(int, Class<T>)`, IIUC the issue described above can also be solved as follows:
```java
invocation.<Runnable>getArgument(0).run()
```
That's admittedly less obvious, but does keep the API smaller. Has this been considered?
While that does work as well, I think consistency with the old API as well as the readability of the former vs the latter would still lead me to conclude that reintroducing is the appropriate solution.

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java
index 95fcfe3236..07af55e377 100644
--- a/src/main/java/org/mockito/MockSettings.java
+++ b/src/main/java/org/mockito/MockSettings.java
@@ -44,6 +44,7 @@
  * Firstly, to make it easy to add another mock setting when the demand comes.
  * Secondly, to enable combining together different mock settings without introducing zillions of overloaded mock() methods.
  */
+@NotExtensible
 public interface MockSettings extends Serializable {
 
     /**
diff --git a/src/main/java/org/mockito/MockitoFramework.java b/src/main/java/org/mockito/MockitoFramework.java
index 58cd4b67ea..da03ef4d80 100644
--- a/src/main/java/org/mockito/MockitoFramework.java
+++ b/src/main/java/org/mockito/MockitoFramework.java
@@ -20,6 +20,7 @@
  * @since 2.1.0
  */
 @Incubating
+@NotExtensible
 public interface MockitoFramework {
 
     /**
diff --git a/src/main/java/org/mockito/MockitoSession.java b/src/main/java/org/mockito/MockitoSession.java
index 9e820f269b..6a529fde43 100644
--- a/src/main/java/org/mockito/MockitoSession.java
+++ b/src/main/java/org/mockito/MockitoSession.java
@@ -88,6 +88,7 @@
  * @since 2.7.0
  */
 @Incubating
+@NotExtensible
 public interface MockitoSession {
 
     /**
diff --git a/src/main/java/org/mockito/internal/invocation/InterceptedInvocation.java b/src/main/java/org/mockito/internal/invocation/InterceptedInvocation.java
index b9cf0721f4..37cc451829 100644
--- a/src/main/java/org/mockito/internal/invocation/InterceptedInvocation.java
+++ b/src/main/java/org/mockito/internal/invocation/InterceptedInvocation.java
@@ -119,6 +119,11 @@ public <T> T getArgument(int index) {
         return (T) arguments[index];
     }
 
+    @Override
+    public <T> T getArgument(int index, Class<T> clazz) {
+        return clazz.cast(arguments[index]);
+    }
+
     @Override
     public Object callRealMethod() throws Throwable {
         if (!realMethod.isInvokable()) {
diff --git a/src/main/java/org/mockito/invocation/InvocationOnMock.java b/src/main/java/org/mockito/invocation/InvocationOnMock.java
index 345ddb9d45..8fabfa0e2d 100644
--- a/src/main/java/org/mockito/invocation/InvocationOnMock.java
+++ b/src/main/java/org/mockito/invocation/InvocationOnMock.java
@@ -7,6 +7,7 @@
 
 import java.io.Serializable;
 import java.lang.reflect.Method;
+import org.mockito.NotExtensible;
 
 /**
  * An invocation on a mock.
@@ -14,6 +15,7 @@
  * <p>
  * A placeholder for mock, the method that was called and the arguments that were passed.
  */
+@NotExtensible
 public interface InvocationOnMock extends Serializable {
 
     /**
@@ -44,12 +46,35 @@ public interface InvocationOnMock extends Serializable {
      *
      * Can lookup in expanded arguments form {@link #getArguments()}.
      *
+     * This method is preferred over {@link #getArgument(int, Class)} for readability. Please read
+     * the documentation of {@link #getArgument(int, Class)} for an overview of situations when
+     * that method is preferred over this one.
+     *
      * @param index argument index
      * @return casted argument at the given index
      * @since 2.1.0
      */
     <T> T getArgument(int index);
 
+    /**
+     * Returns casted argument at the given index. This method is analogous to
+     * {@link #getArgument(int)}, but is necessary to circumvent issues when dealing with generics.
+     *
+     * In general, {@link #getArgument(int)} is the appropriate function to use. This particular
+     * function is only necessary if you are doing one of the following things:
+     *
+     * 1. You want to directly invoke a method on the result of {@link #getArgument(int)}.
+     * 2. You want to directly pas the result of the invocation into a function that accepts a generic parameter.
+     *
+     * If you prefer to use {@link #getArgument(int)} instead, you can circumvent the compilation
+     * issues by storing the intermediate result into a local variable with the correct type.
+     *
+     * @param index argument index
+     * @param clazz clazz to cast the argument to
+     * @return casted argument at the given index
+     */
+    <T> T getArgument(int index, Class<T> clazz);
+
     /**
      * calls real method
      * <p>
diff --git a/src/main/java/org/mockito/session/MockitoSessionBuilder.java b/src/main/java/org/mockito/session/MockitoSessionBuilder.java
index b3a758bac0..b17cfe77d6 100644
--- a/src/main/java/org/mockito/session/MockitoSessionBuilder.java
+++ b/src/main/java/org/mockito/session/MockitoSessionBuilder.java
@@ -7,6 +7,7 @@
 import org.mockito.Incubating;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
+import org.mockito.NotExtensible;
 import org.mockito.exceptions.misusing.UnfinishedMockingSessionException;
 import org.mockito.quality.Strictness;
 
@@ -17,6 +18,7 @@
  * @since 2.7.0
  */
 @Incubating
+@NotExtensible
 public interface MockitoSessionBuilder {
 
     /**
diff --git a/src/main/java/org/mockito/session/MockitoSessionLogger.java b/src/main/java/org/mockito/session/MockitoSessionLogger.java
index e5f40ad565..f876433c57 100644
--- a/src/main/java/org/mockito/session/MockitoSessionLogger.java
+++ b/src/main/java/org/mockito/session/MockitoSessionLogger.java
@@ -6,6 +6,7 @@
 
 import org.mockito.Incubating;
 import org.mockito.MockitoSession;
+import org.mockito.NotExtensible;
 
 /**
  * Logger for {@linkplain org.mockito.quality.MockitoHint hints} emitted when
@@ -17,6 +18,7 @@
  * @since 2.15.0
  */
 @Incubating
+@NotExtensible
 public interface MockitoSessionLogger {
 
     /**


Test patch: