implementing interfaces after the fact

2020-07-13 10:52发布

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.)

6条回答
欢心
2楼-- · 2020-07-13 11:05

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.

查看更多
小情绪 Triste *
3楼-- · 2020-07-13 11:15

(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

查看更多
仙女界的扛把子
4楼-- · 2020-07-13 11:23

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));
查看更多
Deceive 欺骗
5楼-- · 2020-07-13 11:24

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).

查看更多
该账号已被封号
6楼-- · 2020-07-13 11:29

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).

查看更多
Explosion°爆炸
7楼-- · 2020-07-13 11:31

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).

查看更多
登录 后发表回答