【问题标题】:How to call a parametrized getter as a mapper in a java 8 stream?java - 如何在java 8流中调用参数化的getter作为映射器?
【发布时间】:2015-03-12 16:36:43
【问题描述】:

用例

我目前在许多适配器中都有这种模式:

entries.stream()
        .filter(Entry.class::isInstance)
        .map(Entry.class::cast)
        .map(Entry::getFooBar)
        .collect(Collectors.toList());

entrys 是实现特定接口的对象的List。不幸的是,接口 - 它是 3rd 方库的一部分 - 没有定义常见的 getter。要创建我想要的对象列表,我需要搜索它们、强制转换它们并调用适当的 getter 方法。

我打算将它重构为一个辅助类:

public static <T, O> List<O> entriesToBeans(List<T> entries, 
        Class<T> entryClass, Supplier<O> supplier) {
    return entries.stream()
            .filter(entryClass::isInstance)
            .map(entryClass::cast)
            .map(supplier)                  // <- This line is invalid
        .collect(Collectors.toList());
}

然后我会调用这个方法来进行转换:

Helper.entriesToBeans(entries, 
                      Entry_7Bean.class, 
                      Entry_7Bean::getFooBar);

不幸的是,我无法将 getter 传递给重构函数并让 map 调用它,因为 map 需要一个函数。

问题

  • 如何在重构版本中调用 getter?

【问题讨论】:

  • 当源是List&lt;T&gt;entryClassClass&lt;T&gt; 时,为什么要执行.filter(entryClass::isInstance).map(entryClass::cast),换句话说,源已经保证只包含正确键入的项目?
  • @Holger 使用List&lt;? super T&gt; 可能更有意义
  • @assylias: 只是List&lt;?&gt; 因为无论如何都与&lt;T&gt; 没有关系。
  • @Holger 是的 - 你又是对的! ;-)

标签: java java-8 java-stream


【解决方案1】:

类似的方法:

class T {
  public O get() { return new O(); }
}

将映射到Function&lt;T, O&gt;

因此您只需将方法签名更改为:

public static <T, O> List<O> entriesToBeans(List<T> entries,
        Class<T> entryClass, Function<T, O> converter) {

更新:我怀疑强制转换的原因是您的原始列表可能包含不是 T 的元素。因此,您还可以将签名更改为:

public static <T, O> List<O> entriesToBeans(List<?> entries,
        Class<T> entryClass, Function<T, O> converter) {

然后,您可以传递 List&lt;Object&gt;,例如,只保留列表中的 T,进行转换和转换。


作为参考,这是一个工作示例(打印 John, Fred):

static class Person {
  private final String name;
  Person(String name) { this.name = name; }
  String name() { return name; }
}

public static void main(String[] args) {
  List<String> result = entriesToBeans(Arrays.asList(new Person("John"), new Person("Fred")),
                                       Person.class, Person::name);
  System.out.println("result = " + result);
}

public static <T, O> List<O> entriesToBeans(List<?> entries,
        Class<T> entryClass, Function<T, O> converter) {
  return entries.stream()
          .filter(entryClass::isInstance)
          .map(entryClass::cast)
          .map(converter)
          .collect(Collectors.toList());
}

【讨论】:

  • 当我尝试这个时Java抱怨:Helper.entriesToBeans(entries, Entry_7Bean.class, Entry_7Bean::getFooBar);
  • 感谢您的帮助。不幸的是,它仍然在调用端产生错误。 The type Entry_7Bean does not define getFooBar(T) that is applicable。出于某种原因,它将方法引用视为一个对象。
  • 哦,我发现吸气剂必须是静态的……这是有道理的。是否有可能以某种方式让地图使用供应商/转换器调用实例方法?
  • @Pete 忽略我之前的评论。您不需要将其设为静态。请参阅我编辑的答案中的完整示例。
  • @Pete 正如 Holger 所说,List&lt;?&gt; 就足够了。
【解决方案2】:

您应该改为传递Function&lt;T, O&gt;

public static <T, O> List<O> entriesToBeans(List<T> entries, Class<T> entryClass, 
                               Function<T, O> mapper) {
    return entries.stream().filter(entryClass::isInstance)
                  .map(entryClass::cast).map(mapper)
                  .collect(Collectors.toList());
}

【讨论】:

  • ::apply 是什么东西?为什么每个人都对将Function 转换为Function 如此感兴趣?仅仅因为方法引用看起来很花哨?
  • @Holger 该死。我总是这样做:(
  • 令人着迷的是,你不是唯一一个……
猜你喜欢
  • 2017-12-04
  • 2015-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-20
  • 1970-01-01
  • 2015-12-27
相关资源
最近更新 更多