<div dir="ltr">Hi Markus,<div>Thanks for your analysis that a Writer can be seen as a composition as an Appendable, a Flushable, and a Closeable.</div><div>Given this view, I think we should add a Writer.of(Appenable, Flushable, Closeable) to specify the 3 component behaviors of the returned writer.</div><div>Each of the 3 arguments can be null, so that component will be no-op (Writer's Appendable methods only need to trivially return the Writer itself; all other methods return void).</div><div>We will always require all 3 arguments to be passed; a null component means the caller knowingly demands no-op behavior for that component.</div><div>I believe this approach would be safer, and avoids the accidental delegation of unwanted features from a given input Appendable when it happens to duck type.</div><div><br></div><div>Regards,</div><div>Chen Liang</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Sat, Dec 28, 2024 at 10:41 PM Markus KARG <<a href="mailto:markus@headcrashing.eu">markus@headcrashing.eu</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>

  
    
  
  <div>
    <p>Chen,</p>
    <p>thank you for your comments! My ideas to address them are:</p>
    <p>* flush(): If the Appendable implements Flushable, then perform
      Flushable.flush() on it. Otherwise, Writer.flush() will be a no-op
      (besides checking if Writer is open).</p>
    <p>* close(): If the Appendable implements Closeable, then perform
      Closeable.close() on it. Otherwise, Writer.close() will be a no-op
      (besides calling this.flush() if open, and internally marking
      itself as closed).</p>
    <p>* Writer.of(Writer): The original sense of the new API is to
      create a Writer wrapping non-Writers like StringBuilder,
      CharBuffer etc., but not to reduce a Writer to an Appendable (that
      would rather be Appendable.narrow(Writer) or so). IMHO there is
      neither any need nor benefit to return a limited Writer instead of
      the actual writer. So actually I would plea for directly returning
      the given writer itself, so Writer.of(Writer) is a no-op. I do not
      see why someone would intentionally pass in a Writer in the hope
      to get back a more limited, non-flushing / non-closing variant of
      it, and I have a bad feeling about returning a Writer which is
      deliberately cutting away the ability to flush and close without
      any technical need. Maybe you could elaborate on your idea if you
      have strong feelings about that use case?</p>
    <p>* StringWriter: Writer.of() is -by intention- not a "fire and
      forget" drop-in replacement, but a "real" Writer. It comes with a
      price, but in do not see a big problem here. If one is such happy
      with StringWriter that dealing with IOException would be a no-go,
      then simply keep the app as-is. But if one really wants the
      benefits provided by Writer.of(), then dealing with IOExcpetion
      should be worth it. This is a (IMHO very) low price the programmer
      has to pay for the benefit of gaining non-sync, non-copy behavior.
      In most code using StringWriter I have seen so far, IOException
      was dealt with anyways, as the code was mostly IO-bound already
      (it expects "some" Writer, not a StringWriter, as it wants to
      perform I/O, but the target is "by incident" a String).</p>
    <p>To sum up: IMHO still it sounds feasible and the benefits
      outweigh the costs. :-)<br>
    </p>
    <p>-Markus</p>
    <p><br>
    </p>
    <div>Am 28.12.2024 um 01:51 schrieb Chen
      Liang:<br>
    </div>
    <blockquote type="cite">
      
      <div dir="ltr">
        <p dir="ltr">Hi Markus,<br>
          I think the idea makes sense, but it comes with more
          difficulties than in the case of Reader.of. An Appendable is a
          higher abstraction modeling only the character writing
          aspects, without concerns with resource control (such as flush
          or close).</p>
        <p dir="ltr">One detail of note is that Writer itself implements
          Appendable, but I don't think the new method should return a
          Writer as-is; I think it should return another writer whose
          close will not close the underlying writer as we are only
          modelling the appendable behavior without exporting the
          resource control methods. Not sure about flush.</p>
        <p>One use case you have mentioned is StringWriter. StringWriter
          is distinct from StringReader: its write and append methods do
          not throw IOE while the base Writer does. So Writer.of cannot
          adequately replace StringWriter without use-site ugliness,
          until we have generic types that represent the bottom type.</p>
        <p>Regards,</p>
        <p>Chen Liang</p>
      </div>
      <br>
      <div class="gmail_quote">
        <div dir="ltr" class="gmail_attr">On Fri, Dec 20, 2024, 11:12 PM
          Markus KARG <<a href="mailto:markus@headcrashing.eu" target="_blank">markus@headcrashing.eu</a>>
          wrote:<br>
        </div>
        <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Dear
          Sirs,<br>
          <br>
          JDK 24 comes with Reader.of(CharSequence), now let's provide
          the <br>
          symmetrical counterpart Writer.of(Appendable) in JDK 25! :-)<br>
          <br>
          For performance reasons, hereby I like to propose the new
          public factory <br>
          method Writer.of(Appendable). This will provide the same
          benefits for <br>
          writing, that Reader.of(CharSequence) provides for reading
          since JDK 24 <br>
          (see JDK-8341566). Before sharing a pull request, I'd kindly
          like to <br>
          request for comments.<br>
          <br>
          Since Java 1.1 we have the StringWriter class. Since Java 1.5
          we have <br>
          the Appendable interface. StringBuilder, StringBuffer and
          CharBuffer are <br>
          first-class implementations of it in the JDK, and there might
          exist <br>
          third-party implementations of non-String text sinks. Until
          today, <br>
          however, we do not have a Writer for Appendables, but need to
          go costly <br>
          detours.<br>
          <br>
          Text sinks in Java are expected to implement the Writer
          interface. <br>
          Libraries and frameworks expect application code to provide
          Writers to <br>
          consume text produced by the library or framework, for
          example. <br>
          Application code often wants to modify the received text, e.
          g. embed <br>
          received SVG text into in a larger HTML text document, or
          simply forward <br>
          the text as-is to I/O, so StringBuilder or CharBuffer is what
          the <br>
          application code actually uses, but not Strings! In such
          cases, taking <br>
          the StringWriter.toString() detour is common but inefficient:
          It implies <br>
          duplicating the COMPLETE text for the sole sake of creating a
          temporary <br>
          String, while the subsequent processing will copy the data
          anyways or <br>
          just uses a small piece of it. This eats up time and memory
          uselessly, <br>
          and increases GC pressure. Also, StringWriter is synchronized
          (not <br>
          explicitly, but de-facto, as it uses StringBuffer), which
          implies <br>
          another needless slowdown. In many cases, the synchronization
          has no use <br>
          at all, as in real-world applications least Writers are
          actually <br>
          accessed concurrently. As a result, today the major benefit of
          <br>
          StringBuilder over StringBuffer (being non-synchronized)
          vanishes as <br>
          soon as a StringWriter is used to provide its content. This
          means, <br>
          "stringBuilder.append(stringWriter.toString())" imposes slower
          <br>
          performance than essentially needed, in two ways: toString(),
          synchronized.<br>
          <br>
          In an attempt to improve performance of this rather typical
          use case, I <br>
          like to contribute a pull request providing the new public
          factory <br>
          method java.io.Writer.of(Appendable). This is symmetrical to
          the <br>
          solution we implemented in JDK-8341566 for the reversed case:
          <br>
          java.io.Reader.of(CharSequence).<br>
          <br>
          My idea is to mostly copy the existing code of StringWriter,
          but wrap a <br>
          caller-provided Appendable instead of an internally created <br>
          StringBuilder; this strips synchronization; then add optimized
          use for <br>
          the StringBuffer, StringBuilder and CharBuffer implementations
          (in the <br>
          sense of write(char[],start,end) to prevent a char-by-char
          loop in these <br>
          cases).<br>
          <br>
          Alternatives:<br>
          <br>
          - Applications could use Apache Commons IO's
          StringBuilderWriter, which <br>
          is limited to StringBuilder, so is not usable for the
          CharBuffer or <br>
          custom Appendable case. As it is an open-source third-party
          dependency, <br>
          some authors might not be allowed to use it, or may not want
          to carry <br>
          this additional burden just for the sake of this single
          performance <br>
          improvement. In addition, this library is not actively
          modernized; its <br>
          Java baseline still is Java 8. There is no commercial support.<br>
          <br>
          - Applications could write their own Writer implementation.
          Given the <br>
          assumption that this is a rather common use case, this imposes
          <br>
          unjustified additional work for the authors of thousands of <br>
          applications. It is hard to justify why there is a
          StringWriter but not <br>
          a Writer for other Appendables.<br>
          <br>
          - Instead of writing a new Writer factory method, we could
          slightly <br>
          modify StringWriter, so it uses StringBuilder (instead of
          StringBuffer). <br>
          This (still) results in unnecessary duplication of the full
          text at <br>
          toString() and (now also) at getBuffer(), and it will break
          existing <br>
          applications due the missing synchronization.<br>
          <br>
          - Instead of writing a new Writer factory method, we could
          write a new <br>
          AppendableWriter class. This piles up the amount of public
          classes, <br>
          which was the main reason in JDK-8341566 to go with the <br>
          "Reader.of(CharSequence)" factory method instead of the <br>
          "CharSequenceReader" class. Also it would be confusing to have
          <br>
          Reader.of(...) but not Writer.of(...) in the API.<br>
          <br>
          - We could go with a specific Appendable class (like
          StringBuilder) <br>
          instead of supporting all Appendable implementations. This
          would reduce <br>
          the number of applicable use cases daramatically (in
          particular as <br>
          CharBuffer is not supported any more) without providing any
          considerable <br>
          benefit (other than making the OpenJDK-internal source code a
          bit <br>
          shorter). In particular it makes it impossible to opt-in for
          the below <br>
          option:<br>
          <br>
          Option:<br>
          <br>
          - Once we have Writer.of(Appendable), we could replace the
          full <br>
          implementation of StringWriter by synchronized calls to the
          new Writer. <br>
          This would reduce duplicate code.<br>
          <br>
          Kindly requesting comments.<br>
          <br>
          -Markus Karg<br>
          <br>
        </blockquote>
      </div>
    </blockquote>
  </div>

</blockquote></div>