Jackson's TypeReference
permite reificar os parâmetros de tipo genérico de um tipo via subclassificação. Exemplo:
final var typeRef = new TypeReference<List<MyType<MyArg>>>() {};
final List<MyType<MyArg>> list = mapper.readValue(input, typeRef);
final MyType<MyArg> first = list.get(0);
Isso só funciona se o tipo completo for conhecido no momento da subclassificação. Extrair a chamada de conversão em um método genérico não funcionará:
<T> T convert(final String input) {
final var typeRef = new TypeReference<List<MyType<T>>>() {};
final List<MyType<T>> list = mapper.readValue(input, typeRef);
return list;
}
final MyType<MyArg> first = list.get(0);
(porque ele apaga para new TypeReference<List<MyType<Object>>>() {}
, que provavelmente será desserializado para List<MyType<Map<String, Object>>>
).
Quero desserializar e desembrulhar instâncias de tipos genéricos, sem fornecer a assinatura de tipo completa no site de chamada. O site de chamada deve se preocupar apenas com o tipo interno (embrulhado), já que esse é o único tipo com o qual ele irá interagir. Dadas as seguintes definições de registro:
private record MyResponse<T>(MyResult<T> result) {}
private record MyResult<T>(List<T> values) {}
private record MyStringValue(String name) {
@Override public String toString() {
return "'" + name + "'";
}
}
e um método
<T> MyResult<T> convert(final String input, final TypeReference<MyResponse<T>> typeRef) {
try {
return objectMapper.readValue(input, typeRef).result();
} catch (final JsonProcessingException ex) {
throw new RuntimeException(ex);
}
}
como posso desembrulhar o resultado desta função e retornar apenas uma instância de T
, sem fornecer o tipo de referência completo TypeReference<MyResponse<MyResult<T>>>
?
Eu tenho:
<T> List<T> unwrap(final String input, final TypeReference<MyResponse<T>> typeRef) {
return convert(content, typeRef).values();
}
que deve ser chamado como:
final List<MyStringValue> values = unwrap(input, new TypeReference<MyResponse<MyResult<MyStringValue>>>() {});
// or with diamond operator:
final List<AnotherType> values = unwrap(input, new TypeReference<>() {});
Os chamadores de unwrap
não devem precisar saber sobre MyResponse
nem MyResult
. É possível definir unwrap
de tal forma que esses detalhes de implementação fiquem ocultos?
final List<MyStringValue> values = unwrap(input, new TypeReference<MyStringValue>() {});
<T> List<T> unwrap(final String input, final TypeReference<T> typeRef) { // <- callers do not need to know about MyResponse/MyResult
final TypeReference<MyResponse<MyResult<T>>> wrappedTypeRef = typeRef.wrap(MyResponse<MyResult<T>>.class); // obviously not valid Java syntax
return convert(content, typeRef).values();
}
ou estou simplesmente preso à exposição do tipo completo para todos os chamadores deste método devido à eliminação de tipo?
(Ambos MyResponse
e <T>
serão tipos concretos diferentes no código real: MyResponse
tem uma classe pai/filho e T
tem 3 implementações)