Java泛型和流

Java泛型和流,java,generics,lambda,java-stream,Java,Generics,Lambda,Java Stream,我正在构建一种框架,以避免代码重复,在某一点上,我需要将对象Foo列表转换为对象栏列表 我有扩展的数据库实体 public class BaseEntity {...} public class BaseDTO<T extends BaseEntity> {...} 和扩展的表示对象 public class BaseEntity {...} public class BaseDTO<T extends BaseEntity> {...} 及 公共类栏扩展Base

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

我有扩展的数据库实体

public class BaseEntity {...}
public class BaseDTO<T extends BaseEntity> {...}
和扩展的表示对象

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

公共类栏扩展BaseDTO{
公共酒吧(Foo实体,语言环境l){
...
}
}
现在,使用流很容易将Foo列表转换为Bar列表

public abstract ClassThatUsesFooAndBar() {

    public List<Bar> convertFooToBar(List<Foo> list) {
        return list.stream().map(f -> new Bar(f, locale)).collect(...);
    }
}
使用soAndBar()的公共抽象类{
公共列表ConvertoBar(列表){
return list.stream().map(f->newbar(f,locale)).collect(…);
}
}
但是,问题是,这些Foo和Bar实际上是泛型(A和B),因此使用Foo和Bar的类实际上是使用saandb的
类,因此该函数也必须是抽象的,并使用正确的A和B实现作为样板代码实现,因为显然您无法实例化泛型类型

有没有一种方法可以使用泛型/streams/lambda创建一个可以编写一次的函数,这样实现类就不需要重新实现它?函数签名将是

public List<B> convertAToB(List<A> list);
公共列表转换器(列表);
我希望我已经足够清楚我需要什么,如果你需要进一步的解释,请询问


谢谢大家!

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

然而,对于样板文件,由于Java8在接口中的默认方法,您可以做得更好一些。考虑以下接口:

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));
  }
}
然而,如果子类是具体的,那么它就相当简单

public class ConverterClass implements ConversionHandler<String,Integer> {

  @Override
  public Integer constructB(String s, Locale locale) {
    return s.length();
  }
}
公共类ConverterClass实现ConversionHandler{
@凌驾
公共整数构造函数B(字符串s,区域设置){
返回s.length();
}
}

因此,您可能希望搜索的后续内容是一个好的设计模式,使通用对象的构造尽可能可维护和可读。

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

public static <A,B> List<B> convertList(List<A> list, Function<A,B> itemConverter) {
    return list.stream().map(f -> itemConverter.apply(f)).collect(...);
}
公共静态列表转换器列表(列表列表、函数项转换器){
return list.stream().map(f->itemConverter.apply(f)).collect(…);
}
然后你可以这样使用它:

List<Bar> l = convertList(fooList,foo -> new Bar(foo.getBaz()));
List l=convertList(傻瓜,foo->newbar(foo.getBaz());
或者,如果需要,可以在其自己的命名类中提取:

public class Foo2BarConverter implements Function<Foo,Bar> {
    @Override
    public Bar apply(Foo f) {
       return new Bar(f.getBaz());
    }
}
公共类Foo2BarConverter实现函数{
@凌驾
公共酒吧申请(Foo f){
返回新条(f.getBaz());
}
}

顺便说一句,考虑到我们可以用流媒体做什么,创建一个新的列表只是为了得到一个
Bar
对象的具体列表似乎有点浪费。转换后,我可能会直接对列表执行任何操作。

请不要使用单字母类名,因为很难将它们与类型参数区分开来。如果你被随机名称所困扰,你可以使用
Foo
Bar
Baz
。实际上我使用了A和B,因为在讨论结束时它们实际上是类型参数。我将尝试重新措辞…你能更具体地说明泛型的用法吗?您编写的A/B扩展了一个对象,但在您的示例中,A/B没有扩展任何内容。我重新表述了我的问题以使其更清楚:)是的,我正在考虑这样做,因此在子类中,我只需要创建特定的B类型。我把问题的措辞改了一点,所以你说Bs都是泛型。我会在周末考虑,但我认为你的答案最终会是正确的!谢谢:)你说浪费是什么意思?你建议返回一个流供以后处理,而不是创建一个结果列表?@AdamSkywalker是的,它更适合函数方法。我知道了。无论如何,name BaseDTO表示最有可能序列化并发送结果somewhere@AdamSkywalker是的,在某些情况下这是不可避免的。但这些模式往往是“升级”代码以使用lambdas的天真努力的结果,通常是通过更改方法的实现,而不是它们相互调用的方式。同样,有时这是您唯一的选择(如果您的方法是公共API的一部分,并且说它应该返回一个
列表,例如,
),但要知道,如果这些限制不适用,您可以做得更好。@AdamSkywalker是的,DTO将在网格中使用,因此,流程是ORM给出一个实体列表->函数流程,并返回DTO->list,然后将其传递给显示结果的网格
public static <A,B> List<B> convertList(List<A> list, Function<A,B> itemConverter) {
    return list.stream().map(f -> itemConverter.apply(f)).collect(...);
}