Java泛型和流

时间:2015-12-11 17:12:00

标签: java generics lambda java-stream

我正在构建一种框架以避免重复代码,并且在某一点上我需要将对象Foo的列表转换为对象Bar的列表。

我有扩展的数据库实体

public class BaseEntity {...}

扩展的演示文稿对象

public class BaseDTO<T extends BaseEntity> {...}

所以

public class Foo extends BaseEntity {...}

public class Bar extends BaseDTO<A extends BaseEntity> {
    public Bar(Foo entity, Locale l) {
        ...
    }
}

现在,使用流

将Foo列表转换为Bar列表很容易
public abstract ClassThatUsesFooAndBar() {

    public List<Bar> convertFooToBar(List<Foo> list) {
        return list.stream().map(f -> new Bar(f, locale)).collect(...);
    }
}

但是,问题是,这些Foo和Bar实际上是泛型(A和B),因此使用Foo和Bar的类实际上是ClassThatUsesAandB<A extends BaseEntity, B extends BaseDTO>,因此该函数也必须是抽象的并且实现为具有正确A和B实现的样板代码,因为显然您无法实例化泛型类型。

有没有办法使用generics / streams / lambdas来创建一个可以写一次的函数,这样实现类就不需要重新实现它了?函数签名是

public List<B> convertAToB(List<A> list);

我希望我已经足够清楚我需要的东西,如果你需要进一步解释,请询问

谢谢!

2 个答案:

答案 0 :(得分:5)

我认为最简单的方法是使用lambdas进行转换。

    GET my_index/_search
    {
        "query": { 
          "match": {
            "title.title_merge": "anti emetics"
          }
         }
    }

然后你可以像这样使用它:

public static <A,B> List<B> convertList(List<A> list, Function<A,B> itemConverter) {
    return list.stream().map(f -> itemConverter.apply(f)).collect(...);
}

或者,如果您愿意,可以在自己的命名类中提取它:

List<Bar> l = convertList(fooList,foo -> new Bar(foo.getBaz()));

顺便说一下,考虑到我们可以对流做什么,创建一个新列表只是为了拥有一个public class Foo2BarConverter implements Function<Foo,Bar> { @Override public Bar apply(Foo f) { return new Bar(f.getBaz()); } } 对象的物化列表似乎有点浪费。我可能会在转换后直接链接我想要对列表执行的任何操作。

答案 1 :(得分:2)

你的问题最困难的问题实际上不是样板或流,而是泛型。试图做new B有点乱。你不能直接做,任何解决方法都不太干净。

但是,对于样板文件,由于Java 8在接口中的默认方法,您可以做得更好。请考虑以下界面:

public interface ConversionHandler<A,B> {

  B constructB(A a, Locale locale);

  default List<B> convertAToB(List<A> list, Locale locale) {
    return list.stream().map(a -> constructB(a, locale)).collect(Collectors.toCollection(ArrayList::new));
  }
}

现在已完成列表转换样板,您所要做的就是在子类中实现B构造。但是,如果B仍然是通用的,这仍然很棘手。

public class ClassThatUsesAandB<A, B> implements ConversionHandler<A,B> {

  @Override
  public B constructB(A a, Locale locale) {
    return null; //This is tricky
  }
} 

但是,如果子类是具体的,那就很简单了

public class ConverterClass implements ConversionHandler<String,Integer> {

  @Override
  public Integer constructB(String s, Locale locale) {
    return s.length();
  }
}

因此,您可能希望搜索的后续操作是一种很好的设计模式,可以使通用对象的构造尽可能易于维护和可读。