@Override
    public boolean equals(@Nullable Object o) {
      if (!(o instanceof ComparedElements)) {
        return false;
      }
      ComparedElements that = (ComparedElements) o;

      int nArguments = this.aArguments.size();
      if (nArguments != that.aArguments.size()) {
        return false;
      }
      // The arguments must be the same size, but we check anyway.
      if (nArguments != this.bArguments.size() || nArguments != that.bArguments.size()) {
        return false;
      }

      if (!this.a.equals(that.a) || !this.b.equals(that.b)) {
        return false;
      }

      /*
       * The purpose here is just to avoid the infinite recursion that we would otherwise have
       * if Enum<E extends Enum<E>> is compared against itself, for example. If we are able to
       * see that the inner Enum<E> is the same object as the outer one then we don't need a
       * recursive call to compare the "a" Enum<E> against the "b" Enum<E>. The same-object check
       * may not be completely justified, but it relies on the practical assumption that the
       * compiler is not going to conjure up an infinite regress of objects to represent this
       * recursive type. Other comparison methods like comparing their toString() are expensive
       * and not warranted.
       */
      for (int i = 0; i < nArguments; i++) {
        if (this.aArguments.get(i) != that.aArguments.get(i)) {
          return false;
        }
        if (this.bArguments.get(i) != that.bArguments.get(i)) {
          return false;
        }
      }

      return true;
    }
Returns an {@link Equivalence} that can be used to compare types. The standard way to compare
types is {@link javax.lang.model.util.Types#isSameType Types.isSameType}, but this alternative
may be preferred in a number of cases:

<ul>
  <li>If you don't have an instance of {@code Types}.
  <li>If you want a reliable {@code hashCode()} for the types, for example to construct a set
      of types using {@link java.util.HashSet} with {@link Equivalence#wrap(Object)}.
  <li>If you want distinct type variables to be considered equal if they have the same names
      and bounds.
  <li>If you want wildcard types to compare equal if they have the same bounds. {@code
      Types.isSameType} never considers wildcards equal, even when comparing a type to itself.
</ul>
common/src/main/java/com/google/auto/common/MoreTypes.java