2

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();
        }
    }
}
Markus A.
  • 12,349
  • 8
  • 52
  • 116
  • 1
    Can you not just define another method rather than overriding - ie `public void writeMyByteStream(String string, MyByteStream stream)`, since anywhere you invoke it you know you're dealing with a `MyByteStream`? – Bohemian Jun 30 '14 at 01:37
  • @Bohemian That would definitely be a solution. Just wouldn't look as clean. I'm wondering if there is another way to achieve the same thing without having to give up the overriding. But if there isn't, you should write your comment as an answer, so I can accept it. ;) – Markus A. Jun 30 '14 at 01:43
  • Couldn't you add a 3rd write method to StringWriter? – Trent Jun 30 '14 at 04:22
  • Why implement `MyByteStream` at all? Why even *have* `MyByteStream?` – user207421 Jun 30 '14 at 04:25
  • @EJP I wish I could just turn OutputStream into an interface, but I can't... But for all intents and purposes of this question, you can also think of it as just an example. ;) – Markus A. Jun 30 '14 at 04:33
  • @FizzBuzz I was thinking about that, but unfortunately, if I define something like `public void write(String string, StreamType stream)`, I get the error "Method write(String, StreamType) has the same erasure write(String, OutputStream) as another method in type StringWriter". So, unfortunately that's a no-go... Unless there is another way to implement this third method? I wish there was a `` (with OR rather than AND)... – Markus A. Jun 30 '14 at 04:35
  • Hrmm, it's been a while since I've used Java...my thought was to declare the method as `public void write(String string, DebugStream stream)`. – Trent Jun 30 '14 at 04:41
  • @FizzBuzz That, of course, would work. "DebugStream" was just meant to serve as an example here, though, and I'm looking for something a bit more general. ;) – Markus A. Jun 30 '14 at 06:34
  • Hrmm, as in...you would potentially be writing multiple classes that extend `OutputStream` and implement `MyByteStream`? Making that generic is beyond my knowledge unfortunately. – Trent Jun 30 '14 at 06:49

1 Answers1

1

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.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Unfortunately the second option does not support calling write() with an object that implements MyByteStream but does **not** extend OutputStream... In fact, I kind-of need to solve the opposite problem of what option 2 solves: I'm trying to do the exact same thing on two different objects, rather than treating one group of objects in two different ways... – Markus A. Jun 30 '14 at 03:49
  • Naming them differently then seems to be your only option. – Dylan Gattey Jun 30 '14 at 04:19