Jochen, thank you very much for quick and, as always, very knowledgeable response!
On 30. 3. 2016, at 17:12, Jochen Theodorou <[email protected]> wrote: > There are multiple ways to "solve" the problem, but I am afraid, they will > all look like workarounds to you... The traditional Java way would be: > interface InterfaceForAnyClassOfMine { > def getName() > } ... Well of course, but the problem is with 3rd-party classes, which do not implement such an interface (and they might be even final, which prevents also all possible subclassing tricks and actually _forces_ proxying). And generally, it is 3rd-party classes one needs to proxy most time; one's own classes (which I use here to quickly elucidate the gist of the problem) allow lots of other ways solve anything which might proxy be needed for. > Then of course you would not use AnyClassOfMine as base, but > InterfaceForAnyClassOfMine. You can make this a little bit more easy with > @Delegate: > class DumbProxy { > @Delegate InterfaceForAnyClassOfMine server > } I do not like this approach, for it does not cover dynamics of the server -- far as I understand it properly, that is. If the proxy forwards instead, i.e., === class DumbProxy { def server; def methodMissing(name,args) { server."$name"(*args) } } === it covers even e.g., any functionality which has been added to server through its metaclass at runtime. On the other hand, I grant @Delegate one definite advantage (its methods would work when called from Java, which methodMissing can't ever do, I am afraid), and one potential -- it might be considerably faster[*]. > Using runtime traits I would come up with this version: > class AnyClassOfMine { def name } > trait DumbProxy {} > def objects=[new AnyClassOfMine(name:"Proxied") as DumbProxy] > for (DumbProxy o in objects) println "got $o.name" This seems very interesting! I simply _must_ clean up my quagmire of ASTTs so that they work with traits! (While I completely appreciate the strong reasons which led you to sort-of deprecate ASTTs with traits, I must admit for me it was _very_ sad decision, which still, even today, prevents me to use traits and forces me to stick with 3.8 :( ) > I think the basic problem is that you have no multiple inheritance in Java > and only an inconsitent fraction of it in Groovy. Sort of. Myself, I would rather say the problem is basically in the Java terrible howler of making static class/interface relationships overimportant. ObjC does not have MI either, but it can be easily _simulated_ in there using message forwarding. In Groovy (even in plain Java, with some boilerplate code essentially doing by hand what your @Delegate ASTT does), it would work just as well, only if it were possible to typecast a DumbProxy instance to AnyClassOfMine. There is no harm in it, for any method callable of AnyClassOfMine is also callable of DumbProxy; alas, Java atrifically (and dumbly) closes that door by the very “Cannot cast object” exception. > So on the JVM I cannot make a class that implements a class AnyClassOfMine > and DumbProxy at the same time. That is actually not needed at all. All we need is that the JVM calls AnyClassOfMine methods on DumbProxy object. Which is problematic in pure Java (I understand one needs reflexion for that, can be done, but is beyond ugly), but no problem at all in Groovy. Groovy does allow that, and it works excellectly, if we stick with “def”. The problem occurs when there is an existing API which not only _uses_ an instance of AnyClassOfMine, but also _declares it_ as one. E.g., def gimmeObject { return flag?objectOfClassX:proxyToClassX } //[1] works as-is without any glitch. Contrariwise ClassX gimmeObject { return flag?objectOfClassX:proxyToClassX } does not, only due to the blasted “Cannot cast object” exception; if it was not for that thing, it would work just as well as [1] above. > I can do such things with interfaces though. This again forces people to > split their classes in interfaces and implementations, both being first class > citizens. Objective C is much more flexible here. I don't really think so. First, ObjC is actually more rigid in many places (e.g., it _forces_ you to split _any_ class to interface and implementation, willy-nilly :)), but the one (and in this particular context I belive _only_ important) difference is that it freely lets you cast anything to anything. The compiler, of course, behaves accordingly, trusting you you had a good reason to do that typecast. Of course, if you typecast, say, a Map to a String, the program would crash as soon as the real Map gets first String's message it does not implement; but there's no potential problem in that -- nobody sane would do such a typecast (and whomever would, serves him right). On the other hand, it allows proxying. And more. > But it means I cannot apply the same concept to the JVM world... not if I > want to stay with the Java class model. That all said, I am afraid this still applies -- far as I understand, you not only can't make “a class that implements a class AnyClassOfMine and DumbProxy at the same time” (which is not needed), but you also cannot make a class which (a) would implement DumbProxy; any method called on its instance would be interpreted normally (b) at the same moment, would _look to_ the compiler _like_ AnyClassOfMine in the sense it would call AnyClassOfMine's methods on its instances (and use AnyClassOfMine to resolve calls which depend on argument type etc.) Note that (b) would not _change_ the class anyhow; it would still stay a true-blue DumbProxy. The only difference is that compiler would use it _as it were_ an AnyClassOfMine (which it is not). > ... But once you have two super classes you are lost... especially without > the magic -forwardingTargetForSelector:(SEL)sel { return self.server; } I believe _this_ one magic you could implement in Groovy dispatcher, if you wanted, and pretty easily at that. It is nothing but a quick-and-fast shortcut to “methodMissing(name,args) { target."$name"(*args) }”, which only forwards and does not pre-/post-process the method, nothing else. Its purpose is to make it easier for programmer and faster runtime, but the functionality is precisely the same[**]. Thanks and all the best, OC [*] On the other hand, the speed difference should not be that big if the forwarding is done right, that is, at first forward the proxy itself installs the stub through its own metaclass. Nevertheless, that's an optimization which works nicely (most time), but is not relevant to the current debate. [**] Actually in Groovy you would probably have to solve properties separately; ObjC has properties too, but defines them solely through their accessors, which, I believe, is easier -- at least, you don't need separate “methodMissing” and “propertyMissing” (twice) etc., for “methodMissing” catches property accessors just as well as all other methods. But this, too, is completely irrelevant to the current discussion :)
