Estoy desarrollando un código que utiliza genéricos, y uno de mis principios rectores fue hacerlo utilizable para escenarios futuros, y no solo para los de hoy. Sin embargo, varios compañeros de trabajo han expresado que podría haber cambiado la legibilidad en aras de la extensibilidad. Quería reunir algunos comentarios sobre posibles formas de resolver esto.
Para ser específicos, aquí hay una interfaz que define una transformación: comienza con una colección de elementos de origen y aplica la transformación a cada elemento, almacenando los resultados en una colección de destino. Además, quiero poder devolver la colección de destino a la persona que llama, y en lugar de obligarlos a usar una referencia de Colección, quiero que puedan usar el tipo de colección que realmente proporcionaron para la colección de destino.
Finalmente, hago posible que el tipo de elementos en la colección de destino sea diferente del tipo de elementos en la colección de origen, porque tal vez eso es lo que hace la transformación. En mi código, por ejemplo, varios elementos de origen forman un elemento de destino después de la transformación.
Esto produce la siguiente interfaz:
interface Transform<Src, Dst> {
<DstColl extends Collection<? super Dst>> DstColl transform(
Collection<? extends Src> sourceCollection,
DstColl destinationCollection);
}
Traté de ser amable y aplicar el principio PECS de Josh Bloch (productor se extiende, consumidor súper) para asegurarme de que la interfaz sea utilizable con súper y subtipos cuando sea apropiado. El resultado final es algo monstruoso.
Ahora, hubiera sido bueno si pudiera extender esta interfaz y especializarla de alguna manera. Por ejemplo, si realmente no me importa jugar bien con los subtipos de los elementos de origen y los supertipos de los elementos de destino, podría tener:
interface SimpleTransform<Src, Dst> {
<DstColl extends Collection<Dst>> DstColl transform(
Collection<Src> sourceCollection,
DstColl destinationCollection);
}
Pero no hay forma de hacerlo en Java. Quiero hacer que las implementaciones de esta interfaz sean algo que otros realmente considerarían hacer, en lugar de correr con miedo. He considerado varias opciones:
- No devuelva la colección de destino. Parece extraño dado que haces una transformación pero no recuperas nada.
- Tenga una clase abstracta que implemente esta interfaz, pero luego traduzca los parámetros a algo más fácil de usar y llame a otro método "translateImpl ()" que tenga la firma más simple y, por lo tanto, presente una carga cognitiva menor para los implementadores. Pero entonces es extraño tener que escribir una clase abstracta solo para hacer que una interfaz sea fácil de usar.
- Olvídese de la extensibilidad y solo tenga la interfaz más simple. Posiblemente junte eso con no devolver la colección de destino. Pero eso limita mis opciones en el futuro.
¿Qué piensas? ¿Me estoy perdiendo un enfoque que podría usar?
Collection<? extends Src> srcCollection
, deberías usarIterable<? extends Src>