println(String) 接口

Interface for println(String)

我想编写 Java 代码(例如,一种方法)来打印一些行。

要打印的对象应由调用者提供。我希望我的代码 关心那个对象究竟是什么,并简单地调用该对象的 println()println(String) 方法。无论该对象是 java.io.PrintStream(例如 System.out)还是 java.io.PrintWriter(例如由调用者使用 new PrintWriter(System.out)new PrintWriter(new ByteArrayOutputStream()) 构造),它都应该起作用。

如果 "printlineable" 对象的潜在 类 将共享一些强制使用 println()println(String) 方法的接口,这将很容易。然而他们没有。

那么我要在签名中放入什么来接收这样的对象而不违反 DRY principle 通过编写两次本质上相同的实现,只是换出类型(因为我在简单重载时必须这样做)函数)?

public void sayHello( ??? outThingy) {
    outThingy.println("Hello World");
    outThingy.println();
    // This just a minimal example.
    // The real implementation might be more involved
    // and non-trivial, so that avoiding duplication
    // becomes a real concern.
};


// sayHello should be usable like this:

sayHello(System.out);


// but also like this:

baos = new ByteArrayOutputStream();
pw = new PrintWriter(baos)

sayHello(pw);

pw.flush();
System.out.println(baos.toString());

或者 PrintStreamPrintWriter 不共享这样一个接口这一事实是否应该被视为表明它们在提供打印行的方式方面不可互换? (而不是在指定这些 类 时出现某种历史疏忽。)

最简单的方法就是用接受 PrintWriter 的版本和接受 PrintStream 的版本重载方法:

public void sayHello(PrintStream outThingy) {
    outThingy.println("Hello World");
    outThingy.println();
};
public void sayHello(PrintWriter outThingy) {
    outThingy.println("Hello World");
    outThingy.println();
};

这里有一个方法可以做到,至少让 outThingy 的客户保持干爽。但是,您需要权衡有效地拥有一对 WET 类。尽管如此,代码量仍然很少。

// Printer allows for a common interface
interface Printer {
  void println(String line);
  void println();
}

// Used with PrintStream
class StreamPrinter implements Printer {
  private PrintStream ps;

  public StreamPrinter(PrintStream ps) {
    this.ps = ps;
  }

  @Override
  public void println(String line) {
    ps.println(line);
  }

  @Override
  public void println() {
    ps.println();
  }
}

// Used with PrintWriter
class TypeWriter implements Printer {
  private PrintWriter pw;

  public TypeWriter(PrintWriter pw) {
    this.pw = pw;
  }

  @Override
  public void println(String line) {
    pw.println(line);
  }

  @Override
  public void println() {
    pw.println();
  }
}

class Talker {
  // This class doesn't care!
  void sayHello(Printer printer) {
    printer.println("Hello world");
    printer.println();
  }
}

您可能对另一种更实用的方法感兴趣。不必担心每种类型提供什么以及如何在它们之间找到共同的 interface,您可以通过使用 ConsumerRunnable 作为println 方法。

// This is the common class
class FuncPrinter {
  private Consumer<String> consumer;
  private Runnable runnable;

  public FuncPrinter(PrintWriter writer) {
    consumer = writer::println;
    runnable = writer::println;
  }

  public FuncPrinter(PrintStream stream) {
    consumer = stream::println;
    runnable = stream::println;
  }

  public void println(String line) {
    consumer.accept(line);
  }

  public void println() {
    runnable.run();
  }
}

class Talker {    
  void sayHello(FuncPrinter fp) {
    fp.println("Hello World");
    fp.println();
  }
}

你可以这样使用它:

Talker t = new Talker();

FuncPrinter fp = new FuncPrinter(System.out);
t.sayHello(fp);

ByteArrayOutputStream ostream = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(ostream);
fp = new FuncPrinter(pw);
t.sayHello(fp);

fp = new FuncPrinter(
  line -> System.out.println(line),
  () -> System.out.println(42));
t.sayHello(fp);