@Nullable
  private Expectation findPreviousNotStrictExpectation(@Nonnull ExpectedInvocation newInvocation) {
    int n = notStrictExpectations.size();

    if (n == 0) {
      return null;
    }

    Object mock = newInvocation.instance;
    String mockClassDesc = newInvocation.getClassDesc();
    String mockNameAndDesc = newInvocation.getMethodNameAndDescription();
    boolean constructorInvocation = newInvocation.isConstructor();

    for (int i = 0; i < n; i++) {
      Expectation previousExpectation = notStrictExpectations.get(i);

      if (isMatchingInvocation(
              mock, mockClassDesc, mockNameAndDesc, constructorInvocation, previousExpectation)
          && isWithMatchingArguments(newInvocation, previousExpectation.invocation)) {
        return previousExpectation;
      }
    }

    return null;
  }
  @Nullable
  Expectation findNotStrictExpectation(
      @Nullable Object mock,
      @Nonnull String mockClassDesc,
      @Nonnull String mockNameAndDesc,
      @Nonnull Object[] args) {
    boolean constructorInvocation = mockNameAndDesc.charAt(0) == '<';
    Expectation replayExpectationFound = null;

    // Note: new expectations might get added to the list, so a regular loop would cause a CME:
    for (int i = 0, n = notStrictExpectations.size(); i < n; i++) {
      Expectation expectation = notStrictExpectations.get(i);

      if (replayExpectationFound != null && expectation.recordPhase == null) {
        continue;
      }

      if (isMatchingInvocation(
              mock, mockClassDesc, mockNameAndDesc, constructorInvocation, expectation)
          && expectation.invocation.arguments.isMatch(args, instanceMap)) {
        if (expectation.recordPhase == null) {
          replayExpectationFound = expectation;
          continue;
        }

        if (constructorInvocation) {
          registerReplacementInstanceIfApplicable(mock, expectation.invocation);
        }

        return expectation;
      }
    }

    return replayExpectationFound;
  }
  void addExpectation(@Nonnull Expectation expectation, boolean strict) {
    ExpectedInvocation invocation = expectation.invocation;
    forceMatchingOnMockInstanceIfRequired(invocation);
    removeMatchingExpectationsCreatedBefore(invocation);

    if (strict) {
      strictExpectations.add(expectation);
    } else {
      notStrictExpectations.add(expectation);
    }
  }
  private void removeMatchingExpectationsCreatedBefore(@Nonnull ExpectedInvocation invocation) {
    Expectation previousExpectation = findPreviousNotStrictExpectation(invocation);

    if (previousExpectation != null) {
      notStrictExpectations.remove(previousExpectation);
      invocation.copyDefaultReturnValue(previousExpectation.invocation);
    }
  }