Let's say I define myself a new type of byte stream (similar to OutputStream):
public interface MyByteStream {
public void write(byte[] data) throws IOException;
}
Also, I have a helper class that can write Strings to a byte stream, and for flexibility, I would like to have two versions of its method, so that it works with either a regular OutputStream or my new MyByteStream:
public class StringWriter {
public void write(String string, MyByteStream stream) throws IOException {
stream.write(string.getBytes());
}
public void write(String string, OutputStream stream) throws IOException {
stream.write(string.getBytes());
}
}
Now, if I have a class that extends OutputStream and implements MyByteStream, like this:
public class DebugStream extends OutputStream implements MyByteStream {
#Override
public void write(int b) throws IOException {
System.out.println(b);
}
}
I can't just call my StringWriter's write method like this:
new StringWriter().write("Hello", new DebugStream());
as it will give me the following error:
The method write(String, MyByteStream) is ambiguous for the type StringWriter
I can resolve the problem by explicitly casting the DebugStream to one of the byte streams, like so:
new StringWriter().write("Hello", (OutputStream) new DebugStream());
new StringWriter().write("Hello", (MyByteStream) new DebugStream());
But since the two methods do the exact same thing anyways, I would much rather not have to do the cast everywhere. Is there some way around this? Like defining one of the methods as preferred for such ambiguous calls? Or maybe some generics-trickery?
Note:
I would like to keep compile-time type-safety alive, so "solutions" along the following lines are out:
public class StringWriter {
public void write(String string, Object stream) throws IOException {
if (stream instanceof OutputStream) {
((OutputStream) stream).write(string.getBytes());
} else if (stream instanceof MyByteStream) {
((MyByteStream) stream).write(string.getBytes());
} else {
throw new IllegalArgumentException();
}
}
}
There are two main options:
1) Name the method differently, rather than overloading the existing one:
public void write(String string, OutputStream stream) {
//
}
public void writeMyByteStream(String string, MyByteStream stream) {
//
}
This is not a good solution, because it pollutes the API with implementation detail, but it's an option.
2) Use one public method, but privately choose how to handle the stream types:
public void write(String string, OutputStream stream) {
if (stream instanceof MyByteStream) {
writeMyByteStream(string, stream);
} else {
writeOutputStream(string, stream);
}
}
private void writeMyByteStream(String string, MyByteStream stream) {
//
}
private void writeOutputStream(String string, OutputStream stream) {
//
}
The cleaner way is this second one, because the API is far simpler and clients don't need to know that the class does any thing special with a MyByteStream. It also means that special support may be withdrawn from the implementation later without changing any client code.
Some may question using instanceof from a style perspective, but when you need it, you need it.
Related
I am building a simple logger class-
public class MyLogger {
private final PrintWriter errorWriter;
public MyLogger(OutputStream outputStream) {
final Writer errorStreamWriter = new OutputStreamWriter(outputStream);
this.errorWriter = new PrintWriter(errorStreamWriter);
}
public void start() {
errorWriter.flush();
}
public void addError(String errorMessage) {
errorWriter.println(errorMessage);
}
public void finish() {
errorWriter.flush();
errorWriter.close();
}
}
Now I want to write a unit test to test whether the streams are getting flushed or not, in short if we comment the following methods-
public void start() {
// errorWriter.flush();
}
public void finish() {
// errorWriter.flush();
// errorWriter.close();
}
then the test should fail, I do not wish to use reflection even if it provides a solution, I feel that this isn't possible as we have no control over the errorWriter and other connected streams won't be flushed when the chained stream is flushed, but still, if there is a solution, kindly let me know.
There are some possiblities how you can test this. I am using Mockito as mocking framework in the examples.
I would prefer solution 1) all other are more or less hacks, but the pattern can be useful for more dificult classes which use database connections or other expensive objects.
You test against the public API of you class(like a black box). The caller did not know anything about the internal PrintWriter and has no need to know anything about it.
However you can write a test to ensure the OutputStream is flushed since this is propagated (at least with standard Java Streams). This would fail if you comment out the flush in the start method.
class MyLoggerTest {
OutputStream out = mock(OutputStream.class);
MyLogger myLogger = new MyLogger(out);
#Test
void should_call_flush_out_stream() throws IOException {
myLogger.start();
verify(out).flush();
}
}
You change your class to have a second protected constructor which accepts a PrintWriter. The public constructor uses this one and you use a mock and the second constructor in your test for the flush. The test is similar to the one in 1) but you use a mock(printWriter.class).
public class MyLogger {
private final PrintWriter errorWriter;
public MyLogger(OutputStream outputStream) {
this(new PrintWriter(new OutputStreamWriter(outputStream)));
}
MyLogger(PrintWriter writer) {
this.errorWriter = writer;
}
....
You did not access the PrintWriter directly from the member variable, but create an internal getter method, which can then be used in a Spy to change the internal representation.
public class MyLogger {
private final PrintWriter errorWriter;
...Constructor...
PrintWriter getWriter() {
return errorWriter;
}
public void start() {
getWriter().flush();
}
...
}
And the test would look like this:
class MyLoggerTest {
OutputStream out = mock(OutputStream.class);
PrintWriter writer = mock(PrintWriter.class);
MyLogger myLogger = spy(new MyLogger(out));
#BeforeEach
void setup() {
when(myLogger.getWriter()).thenReturn(writer);
}
#Test
void should_call_flush_out_stream() {
myLogger.start();
verify(writer).flush();
}
}
I have stumbled upon this problem:
So I have created a class for a custom filtered input stream and overridden the read() method.
I also need to override the read(byte[] b) method in a way that it uses the criteria that i had defined for the read() method but it does not do. I have tried many ways.
public class CustomInput extends FilterInputStream{
protected CustomInput(InputStream in) {
super(in);
}
#Override
public int read() throws IOException {
int y=super.read();
if(y==65) {
return 194;
} else {
return y;
}
}
#Override
public int read(byte[] b) throws IOException{
}
}
I do not know how to make each byte that the method reads whether it is a 65 and return a 194, and if it is not a 65 return whatever it reads just like I have defined in the read() method.
I'd like to write Java code (say, a method) that will print some lines.
The object onto which to print shall be provided by the caller. I'd like my code to not care what exactly that object is and simply call that objects' println() or println(String) methods. It should work whether that object is a java.io.PrintStream (e.g. System.out) or a java.io.PrintWriter (e.g. constructed by the caller with new PrintWriter(System.out) or new PrintWriter(new ByteArrayOutputStream())).
This would be easy if the potential classes of a "printlineable" object would share some interface that mandated the println() and println(String) methods. However they don't.
So what do I put into the signature to receive such an object without violating the DRY principle by writing twice what is essentially the same implementation, just with swapped out types (as I would have to when simply overloading the function)?
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());
Or should the fact that PrintStream and PrintWriter don't share such an interface be treated as indication that they aren't interchangeable in the regard of providing a way to print lines? (Rather than that being some kind of historical oversight back when these classes were specified.)
The easiest way would just to overload the method with a version that accepts a PrintWriter and a version that accepts a PrintStream:
public void sayHello(PrintStream outThingy) {
outThingy.println("Hello World");
outThingy.println();
};
public void sayHello(PrintWriter outThingy) {
outThingy.println("Hello World");
outThingy.println();
};
Here's a way you could do it and at least keep the client of the outThingy DRY. But, you'll make a trade off for effectively having a couple WET classes. Still, the amount of code is minimal.
// 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();
}
}
You might be interested in a different, more functional approach. Instead of worrying about what each type offers and how to find a common interface between them, you can achieve the same thing with less code by using a Consumer and a Runnable as representations of the println methods.
// 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();
}
}
And you could use it like so:
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);
Is there any good way of using try-with-resources when opening an InputStream in a constructor and then passing that to a super constructor?
Basically what I want to do is this:
public class A {
public A(InputStream stream) {
// Do something with the stream but don't close it since we didn't open it
}
}
public class B {
public B(File file) {
// We open the stream so we need to ensure it's properly closed
try (FileInputStream stream = new FileInputStream(file)) {
super(new FileInputStream(file));
}
}
}
But, of course, since super must be the first statement in the constructor this isn't allowed. Is there any good way of achieving this?
Consider using a static factory method instead of using the constructor directly. Make at least B's constructor private, and create a method such as
private B(InputStream is) {
super(is);
// Whatever else is needed
}
public static B newInstance(File file) {
B result;
try (FileInputStream stream = new FileInputStream(file)) {
result = new B(stream);
}
// Further processing
return result;
}
Another way to go :
public class A {
protected A(){
// so he can't be called from the outside, subclass ensure that init is done properly.
}
public A(InputStream stream) {
init(stream);
}
// not be able to call it from outside
protected final init(InputStream is){
//here goes the code
}
}
public class B {
public B(File file) {
// We open the stream so we need to ensure it's properly closed
try (FileInputStream stream = new FileInputStream(file)) {
init(stream);
}
}
}
I'm posting this here as a possible answer, however here i'm consdering :
You can update A's code
You're moving constructor's code to an init method, thanks to protected empty arg constructor, only subclasses have to handle the call to init properly. Some may see that as not so well designed. My point is as soon your subclassing something, you have to know more about it that just when you just using it.
Sadly I do not have a compiler on hand to test on but could you not do as follows.
public class B {
private static InputStream file2stream(File f){
// We open the stream so we need to ensure it's properly closed
try (FileInputStream stream = new FileInputStream(file)) {
return stream;
}catch(/*what you need to catch*/){
//cleanup
// possibly throw runtime exception
}
}
public B(File file) {
super(file2stream(file))
}
}
The method ObjectOutputStream.writeStreamHeader() can be overridden to prepend or append data to the header. However, if that data is based on an argument passed to the derived class's constructor like:
public class MyObjectOutputStream extends ObjectOutputStream {
public MyObjectOutputStream( int myData, OutputStream out ) throws IOException {
super( out );
m_myData = myData;
}
protected void writeStreamHeader() throws IOException {
write( m_myData ); // WRONG: m_myData not initialized yet
super.writeStreamHeader();
}
private final int m_myData;
}
it doesn't work because super() is called before m_myData is initialized and super() calls writeStreamHeader(). The only way I can think to work around this is by using ThreadLocal like:
public class MyObjectOutputStream extends ObjectOutputStream {
public MyObjectOutputStream( int myData, OutputStream out ) throws IOException {
super( thunk( myData, out ) );
}
protected void writeStreamHeader() throws IOException {
write( m_myData.get().intValue() );
super.writeStreamHeader();
}
private static OutputStream thunk( int myData, OutputStream out ) {
m_myData.set( myData );
return out;
}
private static final ThreadLocal<Integer> m_myData = new ThreadLocal<Integer>();
}
This seems to work, but is there a better (less clunky) way?
There is a general way to solve this sort of problem. Make the class and inner class and reference a variable in the outer scope. (Note, this only works with -target 1.4 or greter, which is the default in current versions of javac. With -target 1.3 you will get an NPE.)
public static ObjectOutputStream newInstance(
final int myData, final OutputStream out
) throws IOException {
return new ObjectOutputStream(out) {
#Override
protected void writeStreamHeader() throws IOException {
write(myData);
super.writeStreamHeader();
}
};
}
But, it's probably easier just to write the data out before constructing the ObjectOuputStream.
Couldn't you do it like this. Ignore the writeStreamHeader call from the super constructor and do one yourself, when you have initialized the needed field:
public class MyObjectOutputStream extends ObjectOutputStream {
private boolean initalized = false;
private final int m_myData;
protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException {
super(out);
m_myData = myData;
initalized = true;
writeStreamHeader();
}
protected void writeStreamHeader() throws IOException {
if(!initalized){
return;
}
write( m_myData );
super.writeStreamHeader();
}
}
EDIT:
Or, as suggested by Thilo, it could be written like:
public class MyObjectOutputStream extends ObjectOutputStream {
private final int m_myData;
protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException {
super(out);
m_myData = myData;
write( m_myData );
super.writeStreamHeader();
}
protected void writeStreamHeader() throws IOException {
// work is done in the constructor
}
}
It is generally a bad idea to call non-final methods from the constructor (for exactly the reason you presented).
Can you achieve your custom serialization without extending ObjectOutputStream? I am thinking about stream composition. For example, you could prepend your header by writing it to the underlying OutputStream before ObjectOutputStream does. This can obviously not be done in a subclass of ObjectOutputStream, but it can easily be done from the outside.
out.write(myExtraHeader);
ObjectOutputStream oos = new ObjectOutputStream(out);
If you want, you can wrap this all up nicely behind the ObjectOutput interface as Stu Thompson suggested in his answer, so that it can look to the outside almost like an ObjectOutputStream.
Update:
Looking at the JavaDocs and source for ObjectOutputStream, there is a second (protected) constructor, that does not call writeStreamHeader().
However, this constructor also does not initialize other internal structures. To quote the docs,
it is intended "for subclasses that are completely reimplementing ObjectOutputStream
to not have to allocate private data just used by this implementation of ObjectOutputStream". In this case it also calls some other methods, such as "writeObjectOverride". Messy...
Use composition instead of inheritance.
public class MyObjOutStream implements DataOutput, ObjectOutput, ObjectStreamConstants {
//same interfaces that ObjectOutputStream implements
private ObjectOutputStream objOutStream;
//implement all the methods below
}
Instance the ObjectOutputStream only when you are ready to do so. The remaining methods you need to implement in the interfaces can just call the same methods on objOutStream