Pregunta

UPDATE2: My own version of the adapter class, that only calls instanceof in the constructor and uses a (Java 1.5) delta in the flush() and close() functions (avoiding the need for any reflection or logic after object construction), is included at the bottom of this post. UPDATE1: Marc Baumbach wrote a simple Adapter that is exactly what I need. Included below. Original question follows.


A function that requires a java.lang.Appendable can accept a java.io.Writer, because Writer implements Appendable.

What about the other way around? I am using a function that requires a writer, and I am trying to create another function that calls it, which accepts an appendable and passes it to the original writer-function.

I see that you can extend Writer, which is abstract, and redirect all write(...) functions to their corresponding append(...)-s. But you also have to implement flush() and close(), and I'm clear on how to write those cleanly so this wrapper-class can accept any Appendable.

I'm surprised there isn't anything already out there, either on the web or stackoverflow, or in an existing library, that addresses this. At least not that I could find.

I'd appreciate a little guidance here. Thank you.


Adapter code that answers this question. Written by Marc Baumbach (my own version is below):

import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.io.Writer;

public class AppendableWriterAdapter extends Writer {

      private Appendable appendable;

      public AppendableWriterAdapter(Appendable appendable) {
            this.appendable = appendable;
      }

      @Override
      public void write(char[] cbuf, int off, int len) throws IOException {
            appendable.append(String.valueOf(cbuf), off, len);
      }

      @Override
      public void flush() throws IOException {
            if (appendable instanceof Flushable) {
                  ((Flushable) appendable).flush();
            }
      }

      @Override
      public void close() throws IOException {
            flush();
            if (appendable instanceof Closeable) {
                  ((Closeable) appendable).close();
            }
      }

}

Here is my own version, based on Marc's, that only uses instanceof only in the constructor, and a (Java 1.5) delta in flush() and close(). This is to avoid having to use any logic or reflection after object construction. This is also released as a gist: https://gist.github.com/aliteralmind/8494917

This class contains a demo, followed by two do-nothing deltas (one Flushable, one Closeable), the main function (newWriterForAppendable(apbl)), and then the adapter class itself.

   import  java.io.Closeable;
   import  java.io.Flushable;
   import  java.io.IOException;
   import  java.io.Writer;
/**
   <P>{@code java NewWriterForAppendable}.</P>
 **/
public class NewWriterForAppendable  {
   /**
      <P>Demonstrates {@code newWriterForAppendable(apbl)} for creating a new {@code Writer} that wraps around {@code System.out} (writes to the console).</P>
    **/
   public static final void main(String[] igno_red)  {
      try  {
         NewWriterForAppendable.newWriterForAppendable(System.out).write("hello");
      }  catch(IOException iox)  {
         throw  new RuntimeException("WriterForAppendableXmpl", iox);
      }
   }
   /**
      <P>A {@code Flushable} whose {@code flush()} function does nothing. This is used by {@link #newWriterForAppendable(Appendable ap_bl) newWriterForAppendable}{@code (apbl)} as a (Java 1.5) delta.</P>

      @see  #newWriterForAppendable(Appendable) newWriterForAppendable(apbl)
    **/
   public static final Flushable FLUSHABLE_DO_NOTHING = new Flushable()  {
      public void flush()  {
      }
   };
   /**
      <P>A {@code Closeable} whose {@code close()} function does nothing. This is used by {@link #newWriterForAppendable(Appendable ap_bl) newWriterForAppendable}{@code (apbl)} as a (Java 1.5) delta.</P>

      @see  #newWriterForAppendable(Appendable) newWriterForAppendable(apbl)
    **/
   public static final Closeable CLOSEABLE_DO_NOTHING = new Closeable()  {
      public void close()  {
      }
   };
   /**
      <P>Creates a new {@code java.io.Writer} that wraps around a {@code java.lang.Appendable}. It properly {@link java.io.Writer#flush() flush}es and {@link java.io.Writer#close() close}s appendables that happened to also be {@link java.io.Flushable}s and/or {@link java.io.Closeable Closeable}s. This uses {@code instanceof} only in the constructor, and a delta in {@code flush()} and {@code close()}, which avoids having to use any logic or reflection after object construction.</P>

      <P>This function is released as a <A HREF="https://gist.github.com/aliteralmind/8494917">gist</A>, and is an example of the <A HREF="http://en.wikipedia.org/wiki/Adapter_pattern#Object_Adapter_pattern">Object Adapter pattern</A>. Thanks to <A HREF="http://stackoverflow.com/users/1211906/marc-baumbach">Marc Baumbach</A> on <A HREF="http://stackoverflow.com">{@code stackoverflow}</A> for the assistance. See (viewed 1/18/2014)
      <BR> &nbsp; &nbsp; <CODE><A HREF="http://stackoverflow.com/questions/21200421/how-to-wrap-a-java-lang-appendable-into-a-java-io-writer">http://stackoverflow.com/questions/21200421/how-to-wrap-a-java-lang-appendable-into-a-java-io-writer</A></CODE></P>

      @return  A new writer that uses an appendable to do its output.
      @see  #FLUSHABLE_DO_NOTHING
      @see  #CLOSEABLE_DO_NOTHING
    **/
   public static final Writer newWriterForAppendable(Appendable ap_bl)  {
      return  (new WFA(ap_bl));
   }
   private NewWriterForAppendable()  {
      throw  new IllegalStateException("constructor: Do not instantiate.");
   }
}
class WFA extends Writer  {
   private final Appendable apbl;
   private final Flushable  flbl;
   private final Closeable  clbl;
   public WFA(Appendable ap_bl)  {
      if(ap_bl == null)  {
         throw  new NullPointerException("ap_bl");
      }
      apbl = ap_bl;

      //Avoids instanceof at every call to flush() and close()
      flbl = (Flushable)((ap_bl instanceof Flushable) ? ap_bl
         :  NewWriterForAppendable.FLUSHABLE_DO_NOTHING);
      clbl = (Closeable)((ap_bl instanceof Closeable) ? ap_bl
         :  NewWriterForAppendable.CLOSEABLE_DO_NOTHING);
   }
   @Override
   public void write(char[] a_c, int i_ndexStart, int i_ndexEndX) throws IOException {
      apbl.append(String.valueOf(a_c), i_ndexStart, i_ndexEndX);
   }
   @Override
   public Writer append(char c_c) throws IOException {
      apbl.append(c_c);
      return  this;
   }
   @Override
   public Writer append(CharSequence c_q) throws IOException {
      apbl.append(c_q);
      return  this;
   }
   @Override
   public Writer append(CharSequence c_q, int i_ndexStart, int i_ndexEndX) throws IOException  {
      apbl.append(c_q, i_ndexStart, i_ndexEndX);
      return  this;
   }
   @Override
   public void flush() throws IOException {
      flbl.flush();
   }
   @Override
   public void close() throws IOException {
      flush();
      clbl.close();
   }

}
¿Fue útil?

Solución

Typically in a Writer, the flush() and close() are there to cleanup any additional writes that may not have been committed or sent to the stream. By simply redirecting all of the write methods directly to the append methods in the Appendable you won't have to worry about flush() and close() unless your Appendable implements Closeable and/or Flushable.

A good example is something like BufferedWriter. When you are calling write() on that, it may not be sending all of the bytes to the final output/stream immediately. Some bytes may not be sent until you flush() or close() it. To be absolutely safe, I would test the wrapped Appendable if it is Closeable or Flushable in the corresponding method and cast it and perform the action as well.

This is a pretty standard design pattern called the Adapter pattern.

Here is what is likely a good implementation for this adapter: http://pastebin.com/GcsxqQxj

Otros consejos

You can accept any Appendable and then check if it is a Writer through instanceof. Then do a downcast and call that function that accepts only Writer.

example:

public void myMethod(Appendable app) throws InvalidAppendableException {

   if (app instanceof Writer) {
      someObj.thatMethod((Writer) app);
   } else {
      throw new InvalidAppendableException();
   }
}

Google's Guava has a simple utility to do this: CharStreams.asWriter

The implementation is not the fastest (see), if you want the best performance, you might want to look at spf4j Streams.asWriter

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top