public final TypeRef<? extends T> getSubtype(Class<?> subclass) {
    if (type instanceof WildcardType) {
      Type[] lowerBounds = ((WildcardType) type).getLowerBounds();
      if (lowerBounds.length > 0) {
        TypeRef<? extends T> bound = of(lowerBounds[0]);
        // Java supports only one lowerbound anyway.
        return bound.getSubtype(subclass);
      }
      throw new IllegalArgumentException(subclass + " isn't a subclass of " + this);
    }
    Type componentType = getComponentType(type);
    if (componentType != null) {
      Class<?> subclassComponentType = subclass.getComponentType();
      if (subclassComponentType == null) {
        throw new IllegalArgumentException(
            subclass + " does not appear to be a subtype of " + this);
      }
      // array is covariant. component type is subtype, so is the array type.
      // requireNonNull is safe because we call getArraySubtype only when isArray().
      TypeRef<?> componentSubtype = of(componentType).getSubtype(subclassComponentType);
      // If we are passed with int[].class, don't turn it to GenericArrayType
      return of(newArrayType(componentSubtype.type));
    }

    Class<? super T> rawType = getRawType();
    if (!rawType.isAssignableFrom(subclass)) {
      throw new IllegalArgumentException(subclass + " isn't a subclass of " + this);
    }

    // If both runtimeType and subclass are not parameterized, return subclass
    // If runtimeType is not parameterized but subclass is, process subclass as a parameterized type
    // If runtimeType is a raw type (i.e. is a parameterized type specified as a Class<?>), we
    // return subclass as a raw type
    if (type instanceof Class
        && ((subclass.getTypeParameters().length == 0)
            || (rawType.getTypeParameters().length != 0))) {
      // no resolution needed
      @SuppressWarnings({"unchecked"}) // subclass isn't <? extends T>
      TypeRef<? extends T> result = (TypeRef<? extends T>) of(subclass);
      return result;
    }
    // class Base<A, B> {}
    // class Sub<X, Y> extends Base<X, Y> {}
    // Base<String, Integer>.subtype(Sub.class):

    // Sub<X, Y>.getSupertype(Base.class) => Base<X, Y>
    // => X=String, Y=Integer
    // => Sub<X, Y>=Sub<String, Integer>
    TypeRef<?> genericSubtype = of(toGenericType(subclass));
    @SuppressWarnings({"rawtypes", "unchecked"}) // subclass isn't <? extends T>
    Type supertypeWithArgsFromSubtype = genericSubtype.getSupertype((Class) rawType).type;

    if (genericSubtype.type instanceof WildcardType) {
      @SuppressWarnings({"unchecked"}) // subclass isn't <? extends T>
      TypeRef<? extends T> result = (TypeRef<? extends T>) genericSubtype;
      return result;
    }

    Map<TypeVariableKey, Type> mappings = new HashMap<>();
    populateTypeMappings(mappings, supertypeWithArgsFromSubtype, type);

    return (TypeRef<? extends T>) resolveType0(genericSubtype.type, mappings);
  }
@Test
  public void testGetSubtype() {
    // For issue: https://github.com/apache/fury/issues/1604
    TypeRef<? extends Map<String, Object>> typeRef =
        TypeUtils.mapOf(MapObject.class, String.class, Object.class);
    assertEquals(typeRef, TypeRef.of(MapObject.class));
    assertEquals(
        TypeUtils.mapOf(Map.class, String.class, Object.class),
        new TypeRef<Map<String, Object>>() {});
  }