Community
Participate
Working Groups
The following is an simple example, whcih shows the bug. If using eclipse and AJDT to compile, it will generate an error messge, saying world method must be implemented. public interface hello { //empty interface } //aspectj introduce a method to this interface public privileged aspect aspectWorld { abstract void hello.world(); } //class test implements hello interface, and //method world public class test implements hello{ public void world(){ System.out.println("hello"); } public static void main(String[] args) { test t = new test(); t.world(); } }
You're introducing an abstract method on an interface, and then extending that interface without providing an implementation. So it seems that the compiler is behaving correctly, asking you to provide an implementation to that method, as it would if you had declared the method on the interface itself. Please explain if this is not the behavior that you are expecting
In class test, I provided the implementation for the world method. Therefore, test class already have the implementation, it shouldn't generate error message. If I move the world method to hello interface from the aspect class (comment it out from the aspect class), the sample code will be compiled. I feel the behavior of the code with aspectj should be same as the second case, but in the first case, the aspectj compiler has compilation error.
Declaring an abstract method on an interface is not an idiom that I have seen before. Using an inner aspect to achieve this is more typical, since it puts all of the crosscutting into one place (both the inter-type declaration and the implementation). E.g. for your code public class test implements hello { static aspect WorldImpl { public void hello.world() { // hello is an interface System.out.println("hello"); } } ... } Nevertheless, we will need to add Programming Guide documentation for your case, and either produce a better error message for why it doesn't work or list it as a limitation. It will also need test coverage added. Andy: I'm reassinging compiler-related bugs to you for now, let me know if I should be giving them to someone else.
The reason this is not compiling is that the declaration from the aspect: abstract void hello.world() is not public. This means AspectJ puts something clever in the interface (it does *not* 'auto promote' the declaration to public visibility). If you change the aspect to: public privileged aspect aspectWorld { public abstract void hello.world(); } then the program compiles just fine. The fact that the declaration hasn't been promoted to public like it would have been if declared directly in the interface requires either: 1) Fixing (if the target for an ITD is an interface, we could force it public if it is default visibility) 2) An xlint warning to tell you there are going to be problems because of the visibility. This is discussed in depth in bug 49784.
I've had an email exchange with Erik over this bug, which is worth including here. It discusses the various cases for the visibility of the interface and the visibility of the abstract method ITD'd upon it: --- The meaning of the example depends crucially on two things: whether the abstract inter-type declaration is public, and whether the interface target is public. Here are the cases: (public I) x (public abstract void I.foo() ) This is fine. I assume this works now, and we just inject an abstract declaration named "foo". (package I) x (public abstract void I.foo() ) Same as above. (package I) x (package abstract void I.foo() ) This is meaningful: We're adding a constraint on all implementors of this interface that they include an (at least package-level) implementation of foo. This is also implementable, assuming that we get the whole package in our compilation unit, incremental or no (I'm pretty sure we rely on that assumption elsewhere). The weaver must inject a method public void ajc$interMethodDispatch2$$foo() { this.foo(); } to every direct implementor of I in the package. I'm pretty sure that's why we're generating the mangled method in the interface, we just forgot to inject the dispatch method. Oops... Note that I haven't provided a use-case here... I'm pretty sure this is of very limited usefulness, if at all. But I don't _think_ it's too hard to implement... (public I) x (package abstract void I.foo() ) This is NOT MEANINGFUL, and we should signal an error here. The intent is that we're adding a non-public constraint to all package-level implementors of I. That's all well and good, but it violates the whole reason for using a public interface. If we have something typed to I inside of our package, we have no idea whether it's one of "our" I's, with a definition of foo(), or one of some other package's I's, with no such definition. (We could do a weird runtime thing here, but I can't bring myself to describe the horribleness. Feel free to ask if you care). Anyway, the compiler should definitely just flag this as an error.
Per Eriks description, I have implemented case (4): (public I) x (package abstract void I.foo() ) will cause an error now - and this would have picked up the testcase for which this bug was raised. The problem is case (3): (package I) x (package abstract void I.foo() ) I can probably get it to work (as he says, we don't currently generate the dispatcher method). My concern is that the version of I that is spat out at the end looks like this: interface I { public abstract void ajc$interMethodDispatch2$$foo(); } So we have added a public dispatcher method to the interface. I'm still thinking about whether this is a problem...
Adrian and I have been discussing this. We are leaning towards this rule: If you make an *abstract* inter-type declaration and the target is an interface, then your ITD must be declared public. If you haven't specified public we will put out an error message. This will help us get round the issue discussed here and in bug 49784 where, if you build the interface with the non-public abstract ITD against it into a class file and then sometime later attempt to provide an implementation using the ITD'd form of the interface as binary input to the compile, you get a misleading error like the one reported here. comments?
Fix checked in as discussed yesterday. It is a compiler limitation. The rule is: "An abstract ITDM on an interface must be declared public." I like straightforward rules like that. We can revisit the limitation in future if anyone ever comes up with a suitable use case for supporting non-public abstract ITDMs.
Fix available: BUILD COMPLETE - build.350 Date of build: 08/20/2004 15:02:32 Time to build: 99 minutes 15 seconds Last changed: 08/20/2004 14:31:09 Latest good AspectJ jar available at: download.eclipse.org/technology/ajdt/dev/aspectj-DEVELOPMENT.jar We now police ITDs that can get you into this situation and put out an error message. Here is the error for the example code in this bug: C:\temp\hello.java:14 [error] abstract intertype method declaration 'void hello.world()' on interface hello must be declared public (compiler limitation) class test implements hello{ ^^^ see also: C:\temp\hello.java:8 1 error
Note that I've also inserted the following into the semantics guide, to take care of the fact that non-public abstract onto public interface is a language error, not just a compiler error. The verbiage is obtuse, as usual. The access modifier of abstract inter-type methods has one constraint: It is illegal to declare an abstract non-public inter-type method on a public interface. This is illegal because it would say that a public interface has a constraint that only non-public implementors must fulfill. This would not be compatible with Java's type system.
Fix released as part of AspectJ 1.2.1