20

Suppose you have two interfaces:

interface Readable {
    public void read();
}

interface Writable {
    public void write();
}

In some cases the implementing objects can only support one of these but in a lot of cases the implementations will support both interfaces. The people who use the interfaces will have to do something like:

// can't write to it without explicit casting
Readable myObject = new MyObject();

// can't read from it without explicit casting
Writable myObject = new MyObject();

// tight coupling to actual implementation
MyObject myObject = new MyObject();

None of these options is terribly convenient, even more so when considering that you want this as a method parameter.

One solution would be to declare a wrapping interface:

interface TheWholeShabam extends Readable, Writable {}

But this has one specific problem: all implementations that support both Readable and Writable have to implement TheWholeShabam if they want to be compatible with people using the interface. Even though it offers nothing apart from the guaranteed presence of both interfaces.

Is there a clean solution to this problem or should I go for the wrapper interface?

UPDATE

It is in fact often necessary to have an object that is both readable and writable so simply seperating the concerns in the arguments is not always a clean solution.

UPDATE2

(extracted as answer so it's easier to comment on)

UPDATE3

Please beware that the primary usecase for this is not streams (although they too must be supported). Streams make a very specific distinction between input and output and there is a clear separation of responsibilities. Rather, think of something like a bytebuffer where you need one object you can write to and read from, one object that has a very specific state attached to it. These objects exist because they are very useful for some things like asynchronous I/O, encodings,...

UPDATE4

One of the first things I tried was the same as the suggestion given below (check the accepted answer) but it proved to be too fragile.

Suppose you have a class that has to return the type:

public <RW extends Readable & Writable> RW getItAll();

If you call this method, the generic RW is determined by the variable receiving the object, so you need a way to describe this var.

MyObject myObject = someInstance.getItAll();

This would work but once again ties it to an implementation and may actually throw classcastexceptions at runtime (depending on what is returned).

Additionally if you want a class variable of the type RW, you need to define the generic at the class level.

  • 5
    The phrase is "whole shebang" – kevin cline Oct 16 '13 at 19:18
  • This is a good question, but I think using Readable and 'Writable' as your example interfaces is muddying the waters somewhat since they are usually different roles... – vaughandroid Oct 17 '13 at 9:23
  • @Basueta While the naming has been simplified, readable & writable actually convey my usecase pretty well. In some cases you want read only, in some cases write only and in a surprising amount of cases read & write. – nablex Oct 17 '13 at 9:28
  • I can't think of a time when I've needed a single stream which is both readable & writeable, and judging from other people's answers/comments I don't think I'm the only one. I'm just saying it might be more helpful to choose a less controversial pair of interfaces... – vaughandroid Oct 17 '13 at 11:16
  • @Baqueta Anything to do with java.nio* packages? If you stick to streams the usecase is indeed limited to places where you would use ByteArray*Stream. – nablex Oct 17 '13 at 11:55
19

Yes, you can declare your method parameter as an underspecified type that extends both Readable and Writable:

public <RW extends Readable & Writable> void process(RW thing);

The method declaration looks terrible, but using it is easier than having to know about the unified interface.

  • 2
    I'd much prefers Konrads second approach here: process(Readable readThing, Writable writeThing) and if you must invoke it using process(foo, foo). – Joachim Sauer Oct 16 '13 at 11:52
  • 1
    Isn't the correct syntax <RW extends Readable&Writable>? – COME FROM Oct 17 '13 at 6:33
  • 1
    @JoachimSauer: Why would you prefer an approach that breaks easily over one that's merely visually ugly? If I call process(foo, bar) and foo and bar are different, the method might fail. – Michael Shaw Oct 17 '13 at 10:00
  • @MichaelShaw: what I'm saying is that it should not fail when they are different objects. Why should it? If it does, then I'd argue that process does multiple different things and violates the single-responsibility principle. – Joachim Sauer Oct 17 '13 at 12:23
  • @JoachimSauer: Why wouldn't it fail? for(i = 0; j < 100; i++) is not as useful a loop as for(i = 0; i < 100; i++). The for loop both reads and writes to the same variable, and it doesn't violate the SRP to do so. – Michael Shaw Oct 17 '13 at 14:49
12

If there is a place where you need myObject as both a Readable and Writable you can:

  • Refactor that place? Reading and writing are two different things. If a method does both, perhaps it doesn't follow the single responsibility principle.

  • Pass myObject twice, as a Readable and as a Writable (two arguments). What does the method care whether it is or isn't the same object?

  • 1
    This could work when you use it as an argument and they are separate concerns. However sometimes you really want an object that is readable and writable at the same time (for the same reason you want to use ByteArrayOutputStream for example) – nablex Oct 16 '13 at 11:57
  • How come? Output streams write, as the name indicates - it's the input streams that can read. Same in C# - there's a StreamWriter vs. StreamReader (and many others) – Konrad Morawski Oct 16 '13 at 12:01
  • You write to a ByteArrayOutputStream in order to get at the bytes (toByteArray()). This is equivalent to write + read. The actual functionality behind the interfaces is much of the same but in a more generic manner. Sometimes you will only want read or write, sometimes you will want both. Another example is ByteBuffer. – nablex Oct 16 '13 at 12:07
  • 2
    I recoiled a little at that second point, but after a moment's thought that really doesn't seem like a bad idea. Not only are you separating out reading and writing, you're making a more flexible function and reducing the amount of input state it mutates. – Phoshi Oct 16 '13 at 12:45
  • 2
    @Phoshi The problem is that the concerns are not always separate. Sometimes you want an object that can both read and write and you want the guarantee that it is the same object (e.g. ByteArrayOutputStream, ByteBuffer,...) – nablex Oct 16 '13 at 12:55
4

None of the answers currently address the situation when you don't need a readable or writable but both. You need guarantees that when writing to A, you can read that data back from A, not write to A and read from B and just hope they are actually the same object. Usecases are plentiful, for example everywhere you would use a ByteBuffer.

Anyway, I've nearly finished the module I'm working on and currently I have opted for the wrapper interface:

interface Container extends Readable, Writable {}

Now you can at least do:

Container container = IOUtils.newContainer();
container.write("something".getBytes());
System.out.println(IOUtils.toString(container));

My own implementations of container (currently 3) all implement Container as opposed to the separate interfaces but should someone forget this in an implementation, IOUtils provides a utility method:

Readable myReadable = ...;
// assuming myReadable is also Writable you can do this:
Container container = IOUtils.toByteContainer(myReadable);

I know this isn't the optimal solution but it's still the best way out at the moment because Container still is a fairly large usecase.

  • 1
    I think this absolutely fine. Better than some of the other approaches proposed in other answers. – Tom Anderson Oct 17 '13 at 14:30
0

Given a generic MyObject instance, you're always going to need to know whether it supports reads or writes. So you would have code like:

if (myObject instanceof Readable)  {
    Readable  r = (Readable) myObject;
    readThisReadable( r );
}

In the simple case, I don't think you can improve on this. But if readThisReadable wants to write the Readable to another file after reading it, it gets awkward.

So I'd probably go with this:

interface TheWholeShabam  {
    public boolean  isReadable();
    public boolean  isWriteable();
    public void     read();
    public void     write();
}

Taking this as a parameter, readThisReadable, now readThisWholeShabam, can handle any class that implements TheWholeShabam, not just MyObject. And it can write it if it is writable and not write it if it isn't. (We've got real "Polymorphism".)

So the first set of code becomes:

TheWholeShabam  myObject = ...;
if (myObject.isReadable()
    readThisWholeShebam( myObject );

And you could save a line here by having readThisWholeShebam() do the readability check.

This does mean that our former Readable-only has to implement isWriteable() (returning false) and write() (doing nothing), but now it can go all kinds of places it couldn't go before and all the code that handles TheWholeShabam objects will deal with it without any further effort on our part.

One other thing: if you can handle a call to read() in a class that doesn't read and a call to write() in a class that doesn't write without trashing something, you can skip the isReadable() and isWriteable() methods. This would be the most elegant way to handle it--if it works.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.