Decidí escribir algunas funciones comunes de orden superior en Java (mapa, filtro, reducir, etc.) que son seguras a través de genéricos, y tengo problemas con la coincidencia de comodines en una función particular.Genéricos Java - implementando funciones de orden superior como map
Sólo para estar completa, la interfaz funtor es la siguiente:
/**
* The interface containing the method used to map a sequence into another.
* @param <S> The type of the elements in the source sequence.
* @param <R> The type of the elements in the destination sequence.
*/
public interface Transformation<S, R> {
/**
* The method that will be used in map.
* @param sourceObject An element from the source sequence.
* @return The element in the destination sequence.
*/
public R apply(S sourceObject);
}
La función preocupante es como un mapa , pero en lugar de transformar una colecciónque transforma un Mapa (en un primer momento pensé que debería llamarse mapMap
, pero sonaba tan estúpido que terminé llamándolo remapEntries
).
Mi primera versión era (y tomar una sentada, porque la firma es bastante un monstruo):
/**
* <p>
* Fills a map with the results of applying a mapping function to
* a source map.
* </p>
* Considerations:
* <ul>
* <li>The result map must be non-null, and it's the same object what is returned
* (to allow passing an unnamed new Map as argument).</li>
* <li>If the result map already contained some elements, those won't
* be cleared first.</li>
* <li>If various elements have the same key, only the last entry given the
* source iteration order will be present in the resulting map (it will
* overwrite the previous ones).</li>
* </ul>
*
* @param <SK> Type of the source keys.
* @param <SV> Type of the source values.
* @param <RK> Type of the result keys.
* @param <RV> Type of the result values.
* @param <MapRes>
* @param f The object that will be used to remapEntries.
* @param source The map with the source entries.
* @param result The map where the resulting entries will be put.
* @return the result map, containing the transformed entries.
*/
public static <SK, SV, RK, RV, MapRes extends Map<RK, RV>> MapRes remapEntries(final Transformation<Map.Entry<SK, SV>, Map.Entry<RK,RV>> f, final Map<SK, SV> source, MapRes result) {
for (Map.Entry<SK, SV> entry : source.entrySet()) {
Map.Entry<RK, RV> res = f.apply(entry);
result.put(res.getKey(), res.getValue());
}
return result;
}
Y parece ser bastante correcta, pero el problema es que la transformación utilizado debe coincidir exactamente con el escriba parámetros, dificultando la reutilización de las funciones del mapa para los tipos que son compatibles. Así que he decidido añadir comodines para la firma, y terminó así:
public static <SK, SV, RK, RV, MapRes extends Map<RK, RV>> MapRes remapEntries(final Transformation<? super Map.Entry<? super SK, ? super SV>, ? extends Map.Entry<? extends RK, ? extends RV>> f, final Map<SK, SV> source, MapRes result) {
for (Map.Entry<SK, SV> entry : source.entrySet()) {
Map.Entry<? extends RK, ? extends RV> res = f.apply(entry);
result.put(res.getKey(), res.getValue());
}
return result;
}
Pero cuando estoy tratando de probarlo, la coincidencia de comodines falla:
@Test
public void testRemapEntries() {
Map<String, Integer> things = new HashMap<String, Integer>();
things.put("1", 1);
things.put("2", 2);
things.put("3", 3);
Transformation<Map.Entry<String, Number>, Map.Entry<Integer, String>> swap = new Transformation<Entry<String, Number>, Entry<Integer, String>>() {
public Entry<Integer, String> apply(Entry<String, Number> sourceObject) {
return new Pair<Integer, String>(sourceObject.getValue().intValue(), sourceObject.getKey()); //this is just a default implementation of a Map.Entry
}
};
Map<Integer, String> expected = new HashMap<Integer, String>();
expected.put(1, "1");
expected.put(2, "2");
expected.put(3, "3");
Map<Integer, String> result = IterUtil.remapEntries(swap, things, new HashMap<Integer, String>());
assertEquals(expected, result);
}
El error es:
method remapEntries in class IterUtil cannot be applied to given types
required: Transformation<? super java.util.Map.Entry<? super SK,? super SV>,? extends java.util.Map.Entry<? extends RK,? extends RV>>,java.util.Map<SK,SV>,MapRes
found: Transformation<java.util.Map.Entry<java.lang.String,java.lang.Number>,java.util.Map.Entry<java.lang.Integer,java.lang.String>>,java.util.Map<java.lang.String,java.lang.Integer>,java.util.HashMap<java.lang.Integer,java.lang.String>
¿Alguna pista sobre cómo solucionarlo? ¿O debería renunciar y escribir loops explícitos para esto?^_^
Tome un vistazo a https://github.com/GlenKPeterson/fp4java7 Es funciones de orden superior para Java implementadas como transormaciones perezosas sobre colecciones inmutables (o mutables). También se implementan algunas transformaciones perezosas persistentes. Es una interfaz completamente genérica, aunque algunos modelos fueron apropiados en la implementación. – GlenPeterson
jeje, tienes 3 años de retraso @GlenPeterson;) por cierto, ¡agrega algunas pruebas! : D – fortran