I decided to write some common Higher Order Functions in Java (map, filter, reduce, etc.) that are type safe via generics, and I'm having problems with wildcards matching in one particular function.
Just to be complete, the functor interface is this:
/**
* 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);
}
The troubling function is like a map, but instead of transforming a Collection it transforms a Map (at first I thought it should be called mapMap
, but it sounded so stupid that I ended up calling it remapEntries
).
My first version was (and take a sit, because the signature is quite a monster):
/**
* <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;
}
And it seems to be quite correct, but the problem is that the transformation used must match exactly the type parameters, making difficult to reuse map functions for types that are compatible. So I decided to add wildcards to the signature, and it ended up like this:
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;
}
But when I'm trying to test it, wildcard matching fails:
@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);
}
The error is:
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>
So, any hints on how to fix this? Or should I give up and write explicit loops for this? ^_^
In many programming languages, map is the name of a higher-order function that applies a given function to each element of a collection, e.g. a list or set, returning the results in a collection of the same type. It is often called apply-to-all when considered in functional form.
Any time we pass a lambda expression to a method, that method is a higher-order function. Java 8 supports higher-order functions, along with lambda expressions and functional interfaces.
use generics. Generic Map in simple language can be generalized as: Map< K, V > map = new HashMap< K, V >(); Where K and V are used to specify the generic type parameter passed in the declaration of a HashMap.
Description: A generic map gives the value to a generic. Usually given in an instance but can also appear in a configuration. The values can be given via positional association or via named association. Use of named association is advised to improve readability and reduce the risk of making errors.
In this tutorial, we will learn about the Higher Order Functions in java. Higher-Order Functions are the functions that either take a function as a parameter or return the function as output. We can say that the Higher-Order functions are the functions that operate on the other function. We will take two examples of the Java Higher-Order Functions.
For eg, all the inbuilt collections in java like ArrayList, HashSet, HashMap, etc. use generics. Generic Map in simple language can be generalized as: Map< K, V > map = new HashMap< K, V > (); Where K and V are used to specify the generic type parameter passed in the declaration of a HashMap.
The term generic simply is the idea of allowing the type (Integer, Double, String, etc. or any user-defined type) to be the parameter to methods, class, or interface. For eg, all the inbuilt collections in java like ArrayList, HashSet, HashMap, etc. use generics. Generic Map in simple language can be generalized as:
As iOS developers, we must aware of Higher-order functions. Using higher-order functions in our code enhances the execution speed of our code and speed up our development skills. A higher-order function can be defined as a function that accepts one or more functions as arguments and returns a function as a result.
I think you should take a look to Google Guava API.
There you can find a Function interface similar to your Transformation one. There is also a class Maps with utility methods to create or transform map instances.
You should also consider PECS when implementing methods for generics use.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With