Community
Participate
Working Groups
To declare a new interface method (not its implementation), being explicit works but anything else results in non-obvious compiler or runtime errors. Some combination of more/better errors and applying the implicit interface modifiers (public, abstract) is warranted. i.e., ------ interface I {} aspect A { // only the first variant works public abstract int I.getInt(); abstract int I.getInt(); // runtime AbstractMethodError int I.getInt(); // weak compiler error } class C implements I { public int getInt() { return 1; } } ------ Implicitly adding the public/abstract modifiers would make these like the Java interface declaration that users already know. (Conversely, declaring an interface method private, protected, final, etc. should provoke a compiler error.) See tests/bugs/interAbstract/InterfaceMethodDeclaration*.java
This would be a significant language change if we made it. I'm marking as a P4 enhancement request for now. If you think it should be higher priority, please discuss on users. inter-type declarations on interfaces have much more flexibility that standard Java interface delcarations. We allow all access modifiers (except protected) on these and allow non-abstract methods. We believe this expansion is important to handle many crosscutting concerns well. If we make public/abstract implicit, then it would be hard to specify non- abstract methods, and it would be impossible to specify inter-type declarations with default visibility. Clearly better error messages for these cases would be nice.
Ok, three bugs here, that I should have written separately, as well as cleanly distinguishing the case of declaring methods with bodies, which are not at issue here. The bugs: (1) P2 fix the runtime error (2) P3 better warning for leaving out the abstract modifier (3) P2 resolve the language semantics and update the docs (1) and (2) can be done with or without implicit hoisting, if you can implement it. (1) If we are going to support "abstract int I.getInt();" (default access on abstract methods) without hoisting, let's do it. If we can't, let's issue an error (no hoisting) or hoist (with XLint warning). In any case, this is a P2 because we're now producing bad bytecode. (2) For "int I.getInt();" (no-abstract) case, it requires a better message whether it's a better error or a hoisting with XLint warning, and thus a P3 (users can add comments to the bug if they want a higher priority). For (1) or (2), we should do hoisting for both or neither for the sake of specifying a consistent language rule. As for whether adding public and/or abstract would be a language change (ignoring that this is not yet specified): Adding abstract is not a language change for methods without bodies; that's either supported without message (implicitly adding abstract for interfaces) or with message (error for classes). Similarly, (not) hoisting default to public is either supported without message (because we can make default work) or with a message (error if we don't want to hoist implicitly, or hoist implicitly with XLint warning). In any case, there is no behavior or semantics being prohibited (it just didn't work). Here's a proposed semantics, to be added to the implementation section. If we can implement default abstract methods, then "{or default}" is included. ---- Declaring members of an interface from an aspect is similarly subject to implementation limitations stemming from Java bytecode invariants. Declarations of methods without bodies on interfaces are implicitly public and abstract, as they are on interfaces. Specifying private {or default} access is permitted, but the declaration becomes subject to the limitations for a non-abstract method. If you declare a non-static field or a non-abstract method on an interface, then all of the top-most implementors of that interface must be woven by that same aspect. (A class C is a top-most implementor of an interface I if C implements I and the superclass of C does not implement I.) ---- Following the model of combined bugs for now, I'm raising this to a P2 subject to your fixing the (1) default-access case "abstract int I.getInt()". If you agree to the semantics, I'll just add them to implementation.xml (unless you or Erik does). Then you can leave this as a P3 if you don't get to case (2). Or split out the two other bugs if you like.
The actual bugs have been verified and fixed. I'm now moving this to docs. The simple explanation is: inter-type method declarations always have the same modifiers as amethod declaration would (except that protected is not permitted). The target type of the inter-type declaration is irrelevant for the syntax of semantics of the declaration. In particular, 'public abstract' is neve implied even if the target is an interface.
"It's not dead yet!" :) So now if I say ---- interface I {} aspect A { abstract int I.i(); } class C { int i() {} } ---- I get an error saying C must implement "int i()". The error resolves if the implementation in C is declared public. tests/bugs/interAbstract/InterfaceMethodDeclarationNonPublic.java Should the error message should say that the declaration is implicitly public and the implementation must be public? or is the error check wrong?. Otherwise, I'd like to see an XLint warning for inter-type declarations on interfaces of non-public abstract methods, which says that they are not implicitly public as the user might have expected, both because interface declarations normally are that way, and because it looks like other declarations could be trouble for modular builds (and J2EE). To wit, the interface decompiles as: interface I{ public abstract int ajc$interMethodDispatch2$$getInt(); } This surprised me. It means that interface now cannot be implemented by anything outside the scope of the ajc compilation, and that all top-level implementations of I *must* be recompiled with ajc. That's a different thing than saying for top-level types to inherit a method implementation, they have to be in the scope of the compilation. Do they all have to be in the same compile, or can you compile the interface and the (binary) aspect, and then put the interface on the classpath and the aspect in the aspectpath and compile the other modules with the top-level classes? That's a pain, but it's doable. If it has to be the same compile process, this feature of ajc is unusable for J2EE. Interfaces play the role of spanning namespaces that are built separately (e.g., an EJB and a servlet share the same business interface, but are built in separate compiles). If possible, we should preserve the ability to compile interfaces and then EJBs, servlets, etc., through tool changes or documentation. If it means that the only inter-type declaration on an interface that will work in that context is of a public abstract method, we should document that. Because the "error" won't show up until a subsequent compile (pointing at a funky ajc method name), we should also have an XLint message for any other declaration, for people who want to avoid this problem before it happens.
this bug is related to bug 70794 that I have just been working on. Things seem to have changed slightly since the last comment on this bug. The program: interface I {} aspect A { abstract int I.i(); } class C implements I{ int i() {return 1;} } gives the error: error must implement abstract inter-type declaration: int I.i() And the program: interface I {} aspect A { abstract int I.i(); } class C implements I{ public int i() {return 1;} } also gives the same error. (i.e. visibility modifier in the implementing class doesn't make a difference). I've just been working to fix the error in the case where an ITD puts an abstract method in an interface and then an implementor of that interface *does* provide an implementation. My fix is nonsense I've discovered, but it did lead me to this bug because the test for this bug (49784) failed in the harness when I had my fix in place :) I've also seen the situation Wes describes where you get 'funky' method signatures in the interface if you didn't make your ITD public. Tomorrow I will see if this can be sorted out using a binary weaving approach like Wes talks about: 1) Compile aspect and interface 2) Build aspectpath zip containing the binary aspect 3) Build an implementor of the interface from source with the interface on the classpath and the aspectpath containing the binary aspect. At the moment I could be persuaded to put in an Xlint if we are producing funky code that may byte the user later on :)
The steps I outlined in the last append: 1) Compile aspect and interface 2) Build aspectpath zip containing the binary aspect 3) Build an implementor of the interface from source with the interface on the classpath and the aspectpath containing the binary aspect. do not work. You get an error something like: C:\ajbugs\C.java:1 error Class must implement the inherited abstract method I.ajc$interMethodDispatch2$$foo() class C implements I { 1 error when compiling the implementing class.
I'm closing this bug, as the resolution for bug 70794 handles this one as well. In addition to the compiler limitiation added for that bug, I've added one paragraph to the semantics document 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