【问题标题】:Java8 streams: generic logger method in streamJava8流:流中的通用记录器方法
【发布时间】:2019-12-13 03:15:36
【问题描述】:

我想创建一个在流中打印信息的方法。请看一下我现有的记录器方法。 方法参数必须是通用的,就像我的示例字符串和整数一样。 该方法应该返回原始对象,在我的例子中是字符串。 谁能告诉我我目前的方法有什么问题?

提前非常感谢!

查看记录器方法

Stream<String> stream = Stream.of("A", "BC", "XYZ");

stream.map(t -> logger(t.length()))
   .map(t-> logger(t.substring(0, 2)))
   .collection(Collectors.toList());

public static <T> T logger(T t) {
    System.out.println(t);
    return t;
  }

错误:无法推断映射(函数)的类型参数

【问题讨论】:

  • logger(t.length()) 将返回整数,因此在您的第二张地图中,您将拥有整数,并且您不能从整数调用 substring
  • 您将t.length() 传递给您的logger 函数,这是一个int,所以您的TInteger
  • 旁注:为什么应该记录一些东西的方法也返回一些值?
  • @michalk 我假设他正在尝试完成类似peek(System.out::println) 的事情,但可以保证它会运行,并且同时指定T 的一些Function 而不仅仅是T。跨度>
  • 为什么要记录任何类型? Java 中的几乎所有内容都可以轻松转换为String。例如让logger() 获取String,然后调用logger(String.valueOf(t.length()))

标签: java java-8 functional-programming java-stream


【解决方案1】:

为什么不使用 Stream#peek()?

这看起来像是您可能希望使用.peek() 而不是地图的示例。它专为打印而设计,您无需担心退回任何东西!

它可能看起来更像:

stream.peek(it -> System.out.println(it.length))
    .peek(it -> System.out.println(it.substring(0, 2))
    // presumably other functions here before collecting...
    .collect(Collectors.toList());

在这里查看官方文档:https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer-

回到原来的问题

为了解决您之前的问题,您可能想要更新您的函数,以便它采用 T 的纯示例和 T 的转换。您可以将其更新为如下所示:

public static <T, R> T logger(T t, Function<T, R> transform) {
  R logLine = transform.apply(t);
  System.out.println(logLine);
  return t;
}

你的代码看起来像

stream.map(element -> logger(element, element::length)) // and so on

【讨论】:

  • 请注意,peek 不能保证在 Java 9+ 上执行(没有愚蠢的解决方法)。
  • 谢谢!我的记录器方法也可以吗?它应该喜欢什么?
  • @Zircon 这是一个很好的呼吁,也许可以验证它是否会起作用.. ?@Toark 我已经添加了一个示例,说明如果您保留现有方法结构可能会是什么样子
  • @gar.stauffer 正是我想要的!非常感谢!
  • @Zircon,如果确实是关于count(),那么对于已经知道大小的情况,无需查看元素来计算它们,因此它不会运行任何阶段并获胜't peek()。它也在 count 的 javadoc 中。但是,如果您需要查看所有元素,则只需执行 forEach 并查看它们。如果 terminalOperation 是 findFirstfindAny,则也不需要在所有元素上都使用 peek()。它或多或少说的是你不应该使用count 操作来计算所有元素,你不应该依赖peek(在大多数情况下用于调试)。
【解决方案2】:

我在这里面临的问题是第一个映射是返回一个整数,所以您尝试使用不存在的Integer.substring

在您的情况下,我建议使用 peek 而不是 map https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer-

import java.util.stream.Collectors;
import java.util.stream.Stream;

public class InferType {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("A", "BC", "XYZ");

        stream.peek(t -> logger(t.length()))
                .peek(t-> logger(t.substring(0, 2)))
                .collect(Collectors.toList());
    }

    public static <T> void logger(T t) {
        System.out.println(t);
    }

}

【讨论】:

    【解决方案3】:

    这对我有用

    import java.util.stream.*;
    
    class Main {
      public static void main(String[] args) {
        Stream<String> stream = Stream.of("AX", "BC", "XYZ");
    
    
        stream.map(t -> logger(t))
          .map(t-> logger(t.substring(0, 1)))
          .collect(Collectors.toList());
    
        /* or this
        stream.map(t -> logger(t))
          .collect(Collectors.toList());
        */
    
      }
    
    public static <T> T logger(T t) {
        System.out.println(t);
        return t;
      }
    }
    

    看看stream() map() 的真正作用here。但基本上,它说

    Stream map(Function mapper) 返回一个流,该流由将 > 给定函数应用于该流的元素的结果组成。

    编辑:@Ezequiel 对Integer.substring 提出了一个很好的观点

    【讨论】:

    • stream.map(t -&gt; logger(t.length)): 你是说取值t 并在其上调用logger 方法,但传入它的长度(整数)。当你这样做时,t 现在是t.length。现在,当您致电.map(t-&gt; logger(t.substring(0, 1))) 时,您是在说哦,是的,给我以前的值(现在是 t.length,一个整数)并给我子字符串。但是整数没有子字符串。这就是注释代码仍然可以编译的原因:因为没有额外的处理,您只需将其收集回来。
    猜你喜欢
    • 2017-01-19
    • 2020-02-04
    • 1970-01-01
    • 2017-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多