5

I think, the following can't be done in Java. But I would be happy to learn how to implement something that resembles it.

Suppose we have a class C, that is already used in compiled code. (We can neither change that code nor the original definition of C).

Suppose further there is interesting code that could be re-used, if only C would implement interface I. It is, in fact, more or less trivial to derive D that is just C + the implementation of the interface methods.

Yet, it seems there is no way, once I have a C, to say: I want you to be a D, that is, a C implementing I.

(Side remark: I think the cast (D)c, where c's runtime type is C, should be allowed if D is a C and the only difference to C are added methods. This should be safe, should it not?)

How could one work around this calamity?

(I know of the factory design pattern, but this is not a solution, it seems. For, once we manage to create D's in all places where formerly were C's, somebody else finds another interface J useful and derives E extends C implements J. But E and D are incompatible, since they both add a different set of methods to C. So while we can always pass an E where a C is expected, we can't pass an E where a D is expected. Rather, now, we'd need a new class F extends C implements I,J.)

Ingo
  • 36,037
  • 5
  • 53
  • 100

6 Answers6

10

Couldn't you use a delegate class, i.e. a new class which wraps an instance of "Class C", but also implements "Interface I" ?

public class D implements I {

    private C c;

    public D (C _c) {
        this.c = _c;
    }

    public void method_from_class_C() {
        c.method_from_class_C();
    }
    // repeat ad-nauseum for all of class C's public methods
    ...

    public void method_from_interface_I() {
        // does stuff
    }
    // and do the same for all of interface I's methods too
}

and then, if you need to invoke a function which normally takes a parameter of type I just do this:

result = some_function(new D(c));
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • why not simply "class D extends C implements I"? – dfa Apr 15 '09 at 15:04
  • because it either requires that you construct with 'new D(...)' instead of 'new C(...)', which may not be possible. It's also not possible to do "D d = (D)c" even though D extends C. Downcasts from D to C should be possible, though. – Alnitak Apr 15 '09 at 15:14
  • I tried to explain that before. I get C's from code not under my control and want to pass them (not a new/different object) to code that works with the interfaces. – Ingo Apr 15 '09 at 15:17
  • that's the point of the delegate - yes, it does create a new (temporary) object, but that's necessary because you can't cast a C into a D. – Alnitak Apr 15 '09 at 15:21
  • and the dynamic proxy stuff has just the same properties - you still end up passing around something other than the original object, and on top of that it's far harder to understand... – Alnitak Apr 15 '09 at 15:26
  • Might want to check for null in that code. Possibly better, make the constructor private and use a static creation method that returns null for a null c. – Tom Hawtin - tackline Apr 15 '09 at 15:35
  • I checked that c wasn't null before passing it to the constructor ;-) – Alnitak Apr 15 '09 at 16:36
7

If all that you need to be compatible with is interfaces then no problem take a look at dynamic proxy classes, its basically how you implement interfaces at runtime in java.

if you need similar runtime compatibility with classes I suggest you take a look at cglib or javaassist opensource libraries.

Lesque
  • 773
  • 8
  • 16
MahdeTo
  • 11,034
  • 2
  • 27
  • 28
  • This looks quite complex, but I'll definitely give it a try. Can you say something about runtime cost, i.e. it looks like I need an extra invokation handler instance per object. – Ingo Apr 15 '09 at 15:14
  • 1
    The delegate method would be much simpler, this adds complexity without any benefit for this particular problem. – Robin Apr 15 '09 at 16:24
  • my thought was that another solution was needed other than applying the adapter pattern specifically a runtime solution. maybe a misinterpretation – MahdeTo Apr 16 '09 at 19:46
3

If you (can) manage the ClassLoader that loads your class C then you can try to do some class-loading time shenanigans with bytecode instrumentation to make the class implement the interface.

The same can be done during build-time, of course. It might even be easier this way (as you don't need access to the ClassLoader).

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
2

(Side remark: I think the cast (D)c, where c's runtime type is C, should be allowed if D is a C and the only difference to C are added methods. This should be safe, should it not?)

Not at all. If you could make this cast, then you could compile code that attempted to call one of the "added methods" on this object, which would fail at runtime since that method does not exist in C.

I think you are imagining that the cast would detect the methods that are "missing" from C and delegate them to D automatically. I doubt that would be feasible, although I can't speak to the language design implications.

It seems to me the solution to your problem is:

Define class D, which extends C and implements I
Define a constructor D(C c) which essentially clones the state of the given C object into a new D object.
The D object can be passed to your existing code because it is a C, and it can be passed to code that wants an I because it is an I

Dave Costa
  • 47,262
  • 8
  • 56
  • 72
  • I meant (D)c to actually change the runtime type of c to D (by just overwriting the vtable-pointer, for instance). Of course, this should only be possible if the compiler can prove that a runtime cast from C to D does not change the Cish behaviour of the object, but just adds new functionality. – Ingo Apr 15 '09 at 15:56
  • It's normally only possible to downcast (i.e. from a subclass to its superclass) because that _removes_ functionality and data members. – Alnitak Apr 15 '09 at 16:44
1

I believe what you want is possible by using java.lang.reflect.Proxy; in fact I have done something similar for a current project. However, it's quite a bit of work, and the resulting "hybrid objects" can expose strange behaviour (because method calls on them are routed to different concrete objects, there are problems when those methods try to call each other).

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
0

I think you that can't do it because Java is strictly typed. I believe it can be done in languages like Ruby and Python with a usage of mixins.

As for Java it definitely looks like a good usage for the Adapter design pattern (it was already proposed earlier as a "wrapper" object).

Nikolay Ivanov
  • 8,897
  • 3
  • 31
  • 34