Estou desenvolvendo algum código que utiliza genéricos, e um dos meus princípios orientadores era torná-lo utilizável para cenários futuros, e não apenas para os de hoje. No entanto, vários colegas de trabalho expressaram que eu posso ter trocado a legibilidade em prol da extensibilidade. Eu queria reunir alguns comentários sobre possíveis maneiras de resolver isso.
Para ser específico, aqui está uma interface que define uma transformação - você começa com uma coleção de itens de origem e aplica a transformação a cada elemento, armazenando os resultados em uma coleção de destino. Além disso, quero poder devolver a coleção de destino ao chamador e, em vez de forçá-los a usar uma referência de coleção, quero que eles possam usar qualquer tipo de coleção que eles realmente forneceram para a coleção de destino.
Por fim, possibilito que o tipo de itens na coleção de destino seja diferente do tipo de itens na coleção de origem, porque talvez seja isso que a transformação faça. No meu código, por exemplo, vários itens de origem compõem um item de destino após a transformação.
Isso produz a seguinte interface:
interface Transform<Src, Dst> {
<DstColl extends Collection<? super Dst>> DstColl transform(
Collection<? extends Src> sourceCollection,
DstColl destinationCollection);
}
Tentei ser legal e apliquei o princípio PECS de Josh Bloch (produtor estende, consumidor super) para garantir que a interface seja utilizável com super e subtipos, quando apropriado. O resultado final é uma monstruosidade.
Agora, seria bom se eu pudesse estender essa interface e especializá-la de alguma forma. Por exemplo, se eu realmente não me importo em jogar bem com os subtipos dos itens de origem e os supertipos dos itens de destino, eu poderia ter:
interface SimpleTransform<Src, Dst> {
<DstColl extends Collection<Dst>> DstColl transform(
Collection<Src> sourceCollection,
DstColl destinationCollection);
}
Mas não há como fazer isso em Java. Quero tornar as implementações dessa interface algo que outros realmente considerariam fazer, em vez de correr com medo. Eu considerei várias opções:
- Não retorne a coleção de destino. Parece estranho, dado que você faz uma transformação, mas não recebe nada de volta.
- Tenha uma classe abstrata que implemente essa interface, mas depois converta os parâmetros para algo mais fácil de usar e chame outro método "translateImpl ()", que possui a assinatura mais simples e, portanto, apresenta menos carga cognitiva para os implementadores. Mas é estranho ter que escrever uma classe abstrata apenas para tornar a interface mais amigável.
- Abandone a extensibilidade e tenha apenas a interface mais simples. Possivelmente junte isso sem retornar a coleção de destino. Mas isso limita minhas opções no futuro.
O que você acha? Estou perdendo uma abordagem que eu poderia usar?
Collection<? extends Src> srcCollection
, você deve usarIterable<? extends Src>