diff --git a/moshi/records-tests/src/test/java/com/squareup/moshi/records/RecordsTest.java b/moshi/records-tests/src/test/java/com/squareup/moshi/records/RecordsTest.java index 5703d200f..c4a23a38f 100644 --- a/moshi/records-tests/src/test/java/com/squareup/moshi/records/RecordsTest.java +++ b/moshi/records-tests/src/test/java/com/squareup/moshi/records/RecordsTest.java @@ -156,6 +156,21 @@ public void genericBoundedRecord() throws IOException { assertThat(adapter.fromJson("{\"value\":4}")).isEqualTo(new GenericBoundedRecord<>(4)); } + @Test + public void indirectGenerics() throws IOException { + var value = + new HasIndirectGenerics( + new IndirectGenerics<>(1L, List.of(2L, 3L, 4L), Map.of("five", 5L))); + var jsonAdapter = moshi.adapter(HasIndirectGenerics.class); + var json = "{\"value\":{\"single\":1,\"list\":[2,3,4],\"map\":{\"five\":5}}}"; + assertThat(jsonAdapter.toJson(value)).isEqualTo(json); + assertThat(jsonAdapter.fromJson(json)).isEqualTo(value); + } + + public static record IndirectGenerics(T single, List list, Map map) {} + + public static record HasIndirectGenerics(IndirectGenerics value) {} + @Test public void qualifiedValues() throws IOException { var adapter = moshi.newBuilder().add(new ColorAdapter()).build().adapter(QualifiedValues.class); diff --git a/moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java b/moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java index 6152d10a5..21f305aba 100644 --- a/moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java +++ b/moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java @@ -17,6 +17,7 @@ import static java.lang.invoke.MethodType.methodType; +import com.squareup.moshi.internal.Util; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandle; @@ -24,8 +25,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.RecordComponent; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -54,39 +53,15 @@ final class RecordJsonAdapter extends JsonAdapter { return null; } - Map mappedTypeArgs = null; - if (type instanceof ParameterizedType parameterizedType) { - Type[] typeArgs = parameterizedType.getActualTypeArguments(); - var typeVars = rawType.getTypeParameters(); - mappedTypeArgs = new LinkedHashMap<>(typeArgs.length); - for (int i = 0; i < typeArgs.length; ++i) { - var typeVarName = typeVars[i].getName(); - var materialized = typeArgs[i]; - mappedTypeArgs.put(typeVarName, materialized); - } - } var components = rawType.getRecordComponents(); var bindings = new LinkedHashMap>(); - var constructorParams = new Class[components.length]; + var componentRawTypes = new Class[components.length]; var lookup = MethodHandles.lookup(); for (int i = 0, componentsLength = components.length; i < componentsLength; i++) { RecordComponent component = components[i]; - constructorParams[i] = component.getType(); + componentRawTypes[i] = component.getType(); var name = component.getName(); - var componentType = component.getGenericType(); - if (componentType instanceof TypeVariable typeVariable) { - var typeVarName = typeVariable.getName(); - if (mappedTypeArgs == null) { - throw new AssertionError( - "No mapped type arguments found for type '" + typeVarName + "'"); - } - var mappedType = mappedTypeArgs.get(typeVarName); - if (mappedType == null) { - throw new AssertionError( - "No materialized type argument found for type '" + typeVarName + "'"); - } - componentType = mappedType; - } + var componentType = Util.resolve(type, rawType, component.getGenericType()); var jsonName = name; Set qualifiers = null; for (var annotation : component.getDeclaredAnnotations()) { @@ -129,7 +104,7 @@ final class RecordJsonAdapter extends JsonAdapter { MethodHandle constructor; try { - constructor = lookup.findConstructor(rawType, methodType(void.class, constructorParams)); + constructor = lookup.findConstructor(rawType, methodType(void.class, componentRawTypes)); } catch (NoSuchMethodException | IllegalAccessException e) { throw new AssertionError(e); }