Summary: | [itds] Allow super.method() type calls to overridden ITD methods | ||
---|---|---|---|
Product: | [Tools] AspectJ | Reporter: | Dave Whittaker <dave> |
Component: | Compiler | Assignee: | aspectj inbox <aspectj-inbox> |
Status: | NEW --- | QA Contact: | |
Severity: | enhancement | ||
Priority: | P3 | CC: | aclement, prg, ramnivas |
Version: | 1.6.3 | ||
Target Milestone: | --- | ||
Hardware: | All | ||
OS: | All | ||
Whiteboard: |
Description
Dave Whittaker
2008-11-04 16:09:45 EST
Adding discussion from 2001 on the old aspectj mailing list that may serve as a starting point for the feature and possible caveats to consider: == My initial query == Hello, With AspectJ, a method implementation may be introduced to an interface. I have a question related to the way it behaves and wondering if it is the right behavior/specification or a bug? For example, consider: interface MyIf { } class MyClass implements MyIf { } aspect IntroduceFooToMyIf { public void MyIf.foo() { System.out.println("This is MyIf.foo()"); } } This results in a foo() in MyIf and foo() with implementation in MyClass. So far so good. Now I change MyClass to: class MyClass implements MyIf { public void foo() { System.out.println("This is MyClass.foo()"); } } This result in not replacing MyClass.foo() by one specified in aspect. This is ok too because, MyClass.foo() overrides introduced MyIf.foo(). Now, I change MyClass to: class MyClass implements MyIf { public void foo() { super.foo(); // <------- !!! System.out.println("This is MyClass.foo()"); } } I get a compiler error, saying there is no foo() in super (MyIf). Now, this would be correct from pure Java point-of-view. But with AspectJ, I expected to result in _equivalent_ of class MyClass implements MyIf { public void foo() { System.out.println("This is MyIf.foo()"); <------- System.out.println("This is MyClass.foo()"); } } That is, super.foo() should have been replaced with the introduced code. In other words, introducing a method in MyIf should have behaved as if that method was indeed there. The replaced code for super.foo() would probably have to be coded as a method (just like other code in AspectJ) so that a "return" statement in it will not result in an incorrect behavior. But, that's just details. I am interested knowing your opinions. Thanks and regards. -Ramnivas == Jim Hugunin's reply == This is a great question. What it boils down to is whether or not "super.m()" in AspectJ should be allowed to see concrete methods in interfaces or not. In AspectJ-1.0beta1 the answer is no. super.m() has exactly the same behavior as in pure Java, and it can only see methods in a super class -- NOT in a super-interface. <The rest of this message is my personal opinion and not necessarily those of the rest of the AspectJ team.> I think that you're probably right and that super.m() should be allowed to access concrete methods in interfaces. However, I don't think that we should add this functionality until after AspectJ-1.0. Multiple-inheritance is a well-known quagmire of complexity in OO language design, and is something that should be approached carefully. I'd like to have some calm time to think carefully about any proposal that would expand the power (and the complexity) of AspectJ's current very limited and simple form of multiple-inheritance. As an example of increasing complexity, if we provide the power you suggest here, I can easily see people asking for qualified super calls to disambiguate multiply inherited methods of the same name, something like "ColorMixin.super.getColor()". Fortunately, your proposal for increasing the power of super can be added to AspectJ-1.1 or some other future version in a mostly backwards compatible way. Therefore, my recommendation is that we mostly table this discussion until after AspectJ-1.0.0 is released. The part that I'd like to talk about today is the mostly-backwards compatible issue. In order to be able to add this proposal in a completely backwards compatible way to a future version of AspectJ, there is one situation that we are not treating as an error that we should be treating so. I believe that the following (currently legal in AspectJ-1.0beta1) program should be illegal in AspectJ-1.0: class SuperC { public void m() { ... } } interface I { aspect BODY { public void I.m() { ... } } } class SubC extends SuperC implements I { public void m() { super.m(); //ERROR: ambiguous reference to either I.m() or SuperC.m() } } This change to AspectJ-1.0 would give us the most room to design a clean solution for super and multiple-inheritance at some future time. It might break some existing AspectJ-1.0beta1 programs; however, I don't believe the odds of that are very high. If you have existing code or designs that this would break, please let me know. -Jim == My follow up == Jim, Thanks for replying. I think by simply allowing interfaces to have introduced-implemented -methods, the whole problem of multiple-inheritance is already opened. I have two particular problems: The first one is a practical one. The other is somewhat academic (assuming it is not common for a class to implemente multiple interface with a method of exacly same signature), although important to consider for the final solution. 1. The situation I was trying to use will not be possible (or atleast elegantly possible) unless super.method() problem is fixed. I have an interface and a default implementation (implemented as BODY aspect) of it. In most cases, default is fine. There are, however, a few cases where I need to augment the behavior. Unless I can call super.method(), such augmentation is not possible. 2. You are right. My next question was going to be about possibility of "ColorMixin.super.getColor()" :-) Consider for example, public interface A { public void foo(); static aspect BODY { public void foo() { System.out.println("A.foo()"); } } } public interface B { public void foo(); static aspect BODY { public void foo() { System.out.println("B.foo()"); } } } public class AB implements A, B { // No foo() implemented here } Current AspectJ implementation chooses to not introduce foo() in AB at all. Which is fine, considering that it would not be possible to determine how foo's implemetation should be implemented. However, I would expect AspectJ to allow adding foo() and let user choose which implementations to inherit and in what order. Something like: public void foo() { A.super.foo(); // some more code B.super.foo(); } Thanks and regards. -Ramnivas == Gregor Kiczales' reponse == Aargh! our goal was to stear clear of as many of the hard multiple inheritance (MI) issues as possible. Here's a way of thinking about the language design that I _think_ can help guide us here. Our theory about introduction of public methods is that: - its a rare - it should probably only be done when introducing an implementation for a well-known interface In addition, remember that in our language we have advice. it seems like we can drive our answer to this issue off of these combined facts, and that the answer, at least for now, is that we do not support the full case of _multiple_ inheritance. We support a weaker thing, which allows a class to get orthogonal sets of methods from classes and interfaces. I'm less sure about whether we support overiding of methods inherited from an interface. So if you someone wants to do the additive multiple inheritance thing they can use around/before/after. That means we have less need for the full power of MI. I think what I'm saying is that: - we should signal an error in this case: > > > > class SuperC { > > public void m() { ... } > > } > > > > interface I { > > aspect BODY { > > public void I.m() { ... } > > } > > } > > > > class SubC extends SuperC implements I { > > public void m() { > > super.m(); //ERROR: ambiguous reference to either I.m() or > > SuperC.m() > > } > > } - and we could allow super.method() to work in the simple case, but we could also prohibit it, at least for now, and point people to around. Ramnivas, how does this rationale and set of proposed design decisions fit for you. Can you use advice the way I mention to get the MI effect you need?. == end responses == unsetting the target field which is currently set for something already released |