【问题标题】:from imperativ to functional从命令式到功能性
【发布时间】:2016-03-16 10:18:24
【问题描述】:

经典的命令按预期工作:

static void updateFile(final File pm, final String replace, 
                                            final String replacement) {
  BufferedReader bri = null;
  BufferedWriter out = null;
  try {
     String fName = pm.getPath() + ".new";
     bri = new BufferedReader(new FileReader(pm));
     out = new BufferedWriter(new FileWriter(fName));
     String line = bri.readLine();
     while (line != null) {
        if (line.contains(replace)) {
           line = line.replace(replace, replacement);
        }
        out.write(line);
        out.newLine();
        line = bri.readLine();
     }
  } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
  } finally {
     try {
        out.flush();
        out.close();
        bri.close();
     } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
     }
  }
}

现在尝试替换:

  static void updateFileLambda(final File pm, final String replace, 
                          final String replacement) throws Exception {
  String fName = pm.getPath() + ".new";
  BufferedReader bri = new BufferedReader(new FileReader(pm));
  BufferedWriter out = new BufferedWriter(new FileWriter(fName));

  bri.lines().filter(ln -> ln.contains(replace))
             .map(ln -> ln.replace(replace, replacement))
             .forEach(ln -> { 
                               out.write(ln);
                               out.newLine();
          });
}

1.) 这引发了几个问题并且无法编译,因为 out.write 和 out.writeLine Eclipse mars 告诉我它应该被 try 包围 - 尽管声明了 throws Ecxeption 或者声明被 try 包围-catch 与经典版本一样。

2.) 每一行都应该写入新文件。旧的和新的应该只差一个修改的行。我怀疑,show 解决方案只会将修改后的行写入新文件。

因此,如果有人知道如何正确实现,我将不胜感激!

【问题讨论】:

    标签: java lambda functional-programming


    【解决方案1】:

    Stream.forEach 接受 Consumer 参数。所以,

             .forEach(ln -> { 
                 out.write(ln);
                 out.newLine();
             }
    

    等价于

             .forEach( new Consumer<String>() {
                 @Override
                 public void accept( String ln ) {
                     out.write( ln );
                     out.newLine();
                 }
             } );
    

    而且,accept 方法不能抛出任何已检查的异常,这就是需要 try-catch 的原因。

    此外,由于您没有关闭bri,因此永远不会写入输出,从而产生一个空文件。您可以使用try-with-resources statement 自动完成此操作:

    static void updateFileLambda( final File pm, final String replace, final String replacement )
        throws Exception
    {
        String fName = pm.getPath() + ".new";
    
        try (BufferedReader bri = new BufferedReader( new FileReader( pm ) );
            BufferedWriter out = new BufferedWriter( new FileWriter( fName ) ))
        {
    
            bri.lines()
            .filter( ln -> ln.contains( replace ) )
            .map( ln -> ln.replace( replace, replacement ) )
            .forEach(
                ln -> {
                    try
                    {
                        out.write( ln );
                        out.newLine();
                    }
                    catch ( IOException e )
                    {
                        throw new RuntimeException( e );
                    }
    
                } );
        }
    }
    

    【讨论】:

    • 好的,到目前为止已经很清楚了。但是这种语法仍然存在一些问题。在经典示例中,我关闭了 finally 块中的流。 (使用我们习惯的丑陋的附加 try - catch。)但是我应该在哪里使用流语法实现相同的效果。到目前为止,我不在乎它是否有效。目的只是找出如何以声明的方式编写这样的案例。曾经使它更易于阅读,并为编译器打开路径以优化它以进行多线程处理。在你展示的时候添加 try-catch,让我们回到经典。
    • 没错,可惜了。您会认为 IO 异常在处理流中很常见。 This Q/A 有更多信息。至于最后的结束,这基本上就是新的 try-with-resources 所做的:try ( AutoCloseable x = ... ) {} 变为 try { AutoCloseable x = .. } finally { x.close() } - 它就像功能接口一样,只是语法糖(AFAIK)。当然,您仍然可以尝试/最终并自己关闭流。
    【解决方案2】:

    其实关键是使用try-with-resource语句。在这种情况下,干净的声明式表单所需的第二步是使用包含行尾字符的 PrintWriter,而不是需要额外的 out.newLine() 的 BufferedWriter。这样做的结果会导致以下 sn-p:

    try (
            BufferedReader bri = new BufferedReader(new FileReader(pm));
            PrintWriter out = new PrintWriter(new FileWriter(newFile), true)) {
            bri.lines()
               .map(ln -> ln.replace(replace, replacement))
               .forEach(ln -> out.println(ln));
      }
    

    不使用 try-with 资源,会将您发送到要求为匿名内部类最终的变量的地狱,然后在 try-catch 的 finally 块中不可见以关闭流。结论:不错的解决方案是可能的,但一旦不适合标准功能接口就很棘手!正如您在上述问答中所展示的那样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-12
      • 2011-10-27
      • 1970-01-01
      • 2020-09-23
      • 1970-01-01
      • 2017-10-08
      • 2014-06-09
      • 2019-03-01
      相关资源
      最近更新 更多