问题
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> <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();
}
}
回答1:
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
回答2:
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();
}
}
回答3:
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
来源:https://stackoverflow.com/questions/21200421/how-to-wrap-a-java-lang-appendable-into-a-java-io-writer