Java泛型 - 实现像地图这样的高阶函数 [英] Java generics - implementing higher order functions like map

查看:126
本文介绍了Java泛型 - 实现像地图这样的高阶函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我决定在Java中编写一些通用的高阶函数(map,filter,reduce等),这些函数是通过泛型类型安全的,而且我在通配符匹配中遇到了一些问题。 b
$ b

为了完整,functor的界面是这样的:

  / ** 
*包含用于将序列映射到另一个序列的方法的接口。
* @param< S>源序列中元素的类型。
* @param< R>目标序列中元素的类型。
* /
public interface转换< S,R> {

/ **
*将在地图中使用的方法。
* @param sourceObject源序列中的元素。
* @return目标序列中的元素。
* /
public R apply(S sourceObject);
}

麻烦的功能就像是一张地图,但它不是转换一个 Collection 它转换一个 Map (起初我认为它应该叫做 mapMap ,但它响了所以我最终称它为 remapEntries )。



我的第一个版本是(并且坐下来,因为签名相当怪物):

  / ** 
*< p>
*使用将映射函数应用于
*源图的结果填充地图。
*< / p>
*注意事项:
*< ul>
*< li>结果映射必须为非null,并且它是相同的对象返回的
*(以允许传递未命名的新Map作为参数)。< / li>
*< li>如果结果地图已包含某些元素,则不会先清除
*。< / li>
*< li>如果各个元素具有相同的键,则只有最后一个给定
*源迭代顺序的条目将出现在结果映射中(它将覆盖之前的元素)< /立GT;
*< / ul>
*
* @param< SK>源密钥的类型。
* @param< SV>源值的类型。
* @param< RK>结果键的类型。
* @param< RV>结果值的类型。
* @param< MapRes>
* @param f将用于重映射条目的对象。
* @param source包含源条目的地图。
* @param result结果条目放置的地图。
* @返回包含已转换条目的结果图。
* /
public static< SK,SV,RK,RV,MapRes扩展Map< RK,RV>> (Map.Entry< SK,SV>,Map.Entry< RK,RV>> f,final Map< SK,SV> source,MapRes result){
SK,SV>条目:source.entrySet()){
Map.Entry< RK,RV> res = f.apply(entry);
result.put(res.getKey(),res.getValue());
}
返回结果;
}

它似乎是非常正确的,但问题在于所使用的转换必须完全匹配类型参数,因此难以对兼容的类型重新使用映射函数。所以我决定在签名中添加通配符,结果如下:

  public static< SK,SV,RK ,RV,MapRes扩展Map< RK,RV>> MapRes remapEntries(final Transformation< superMap.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< ;?扩展RK,?延伸RV> res = f.apply(entry);
result.put(res.getKey(),res.getValue());
}
返回结果;
}

但是当我试图测试它时,通配符匹配失败: p>

  @Test 
public void testRemapEntries(){
Map< String,Integer> things = new HashMap< String,Integer>();
things.put(1,1);
things.put(2,2);
things.put(3,3);

转换< 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()); //这只是Map.Entry
}
}的默认实现;

映射< Integer,String> expected = new HashMap< Integer,String>();
expected.put(1,1);
expected.put(2,2);
expected.put(3,3);

映射< Integer,String> result = IterUtil.remapEntries(swap,things,new HashMap< Integer,String>());
assertEquals(预期,结果);
}

错误是:

 类IterUtil中的方法remapEntries不能应用于给定类型
required:Transformation< ;?超级java.util.Map.Entry< ;?超级SK,?超级SV>,?扩展java.util.Map.Entry< ;?扩展RK,?扩展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 LT; java.lang.String中,java.lang.Integer中>,&的java.util.HashMap LT; java.lang.Integer中,java.lang.String中>

那么,如何解决这个问题呢?或者我应该放弃并为此写出明确的循环? ^ _ ^

解决方案

我想你应该看看 Google Guava API

在那里你可以找到功能界面与您的转型类似一。还有一类地图 a>用实用方法创建或转换地图实例。



您还应该考虑 PECS 在实现泛型使用方法时使用。


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? ^_^

解决方案

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.

这篇关于Java泛型 - 实现像地图这样的高阶函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆