code: public static boolean isEqual(Type type1, Type type2)
    {
      if(type1 == null) {
        return type2 == null;
      }
      if(type2 == null) {
        return type1 == null;
      }
      if(!(type1 instanceof ParameterizedType)) {
        return type1.equals(type2);
      }
  
      // handle parameterized types
  
      if(!(type2 instanceof ParameterizedType)) {
        return false;
      }
      ParameterizedType ptype1 = (ParameterizedType)type1;
      ParameterizedType ptype2 = (ParameterizedType)type2;
      if(!ptype1.getRawType().equals(ptype2.getRawType())) {
        return false;
      }
      Type[] atype1 = ptype1.getActualTypeArguments();
      Type[] atype2 = ptype2.getActualTypeArguments();
      if(atype1.length != atype2.length) {
        return false;
      }
      for(int i = 0; i < atype1.length; ++i) {
        if(!isEqual(atype1[i], atype2[i])) {
          return false;
        }
      }
      return true;
    }
docs: Test two types for equality. This predicate takes care of parameterized types by comparing both raw type and actual
  type arguments. If an actual type argument is parameterized on its own this method is executed recursively.
  <p>
  If both types are null returns true.
  
  @param type1 first type, null accepted,
  @param type2 second type, null accepted.
  @return true if first type equals the second one.