@Override
  public final void isEqualTo(@Nullable Object other) {
    @SuppressWarnings("UndefinedEquals") // the contract of this method is to follow Multimap.equals
    boolean isEqual = Objects.equal(actual, other);
    if (isEqual) {
      return;
    }

    // Fail but with a more descriptive message:
    if ((actual instanceof ListMultimap && other instanceof SetMultimap)
        || (actual instanceof SetMultimap && other instanceof ListMultimap)) {
      String actualType = (actual instanceof ListMultimap) ? "ListMultimap" : "SetMultimap";
      String otherType = (other instanceof ListMultimap) ? "ListMultimap" : "SetMultimap";
      failWithoutActual(
          fact("expected", other),
          fact("an instance of", otherType),
          fact("but was", actualCustomStringRepresentationForPackageMembersToCall()),
          fact("an instance of", actualType),
          simpleFact(
              lenientFormat(
                  "a %s cannot equal a %s if either is non-empty", actualType, otherType)));
    } else if (actual instanceof ListMultimap) {
      containsExactlyEntriesIn((Multimap<?, ?>) checkNotNull(other)).inOrder();
    } else if (actual instanceof SetMultimap) {
      containsExactlyEntriesIn((Multimap<?, ?>) checkNotNull(other));
    } else {
      super.isEqualTo(other);
    }
  }
@Test
  public void isEqualTo_failsWithSameToString() {
    expectFailureWhenTestingThat(ImmutableMultimap.of(1, "a", 1, "b", 2, "c"))
        .isEqualTo(ImmutableMultimap.of(1L, "a", 1L, "b", 2L, "c"));
    assertFailureKeys("missing", "unexpected", "---", "expected", "but was");
    assertFailureValue("missing", "[1=a, 1=b, 2=c] (Map.Entry<java.lang.Long, java.lang.String>)");
    assertFailureValue(
        "unexpected", "[1=a, 1=b, 2=c] (Map.Entry<java.lang.Integer, java.lang.String>)");
    assertFailureValue("expected", "{1=[a, b], 2=[c]}");
    assertFailureValue("but was", "{1=[a, b], 2=[c]}");
  }