Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-users] Question about interface inheritance and introduction

OK, put your thinking caps on for this one.  I'm getting introductions
on a type that doesn't match the declare parents type expression.

Consider the following Java artifacts, all defined in a package called
"example":

public class Foo() {}
public class Bar() {}

public interface HasFoo { Foo getFoo(); }
public interface HasBar extends HasFoo { Bar getBar(); }

public @interface Fooable {} // assume target is TYPE and retention RUNTIME
public @interface Barable {} // assume target is TYPE and retention RUNTIME

@Fooable public class MyFooable {}

@Barable public class MyBarable {}

Now, consider that I want to introduce an implementation of HasFoo to
classes annotated with @Fooable:

public aspect HasFooIntro {
	declare parents: (@Fooable *) implements HasFoo;

	private boolean HasFoo.returnNull = false;
	
	public Foo HasFoo.getFoo() {
		Foo foo = returnNull ? null : new Foo();
		returnNull = !returnNull;
		return foo;
	}
}

...and HasBar to classes annotated with @Barable:

public aspect HasBarIntro {
	declare parents: (@Barable *) implements HasBar;

	public Bar HasBar.getBar() {
		return getFoo() == null ? new Bar() : null;
	}
}

When the compiler completes processing, I end up with expected results
when I call "javap example.MyFooable":

Compiled from "MyFooable.java"

public class example.MyFooable
    extends java.lang.Object
    implements example.HasFoo
{
    public boolean ajc$interField$example_HasFooIntro$example_HasFoo$returnNull;
    public example.MyFooable();
    public boolean
ajc$interFieldGet$example_HasFooIntro$example_HasFoo$returnNull();
    public void
ajc$interFieldSet$example_HasFooIntro$example_HasFoo$returnNull(boolean);
    public example.Foo getFoo();
}

However, when I call "javap example.MyBarable", I get unexpected
results.  Even though the type expression in HasFooIntro's "declare
parents" doesn't match MyBarable, the introduced methods and fields of
HasFooIntro are present in MyBarable:

Compiled from "MyBarable.java"

public class example.MyBarable
    extends java.lang.Object
    implements example.HasBar
{
  public boolean ajc$interField$example_HasFooIntro$example_HasFoo$returnNull;
  public example.MyBarable();
  public boolean
ajc$interFieldGet$example_HasFooIntro$example_HasFoo$returnNull();
  public void ajc$interFieldSet$example_HasFooIntro$example_HasFoo$returnNull(boolean);
  public example.Bar getBar();
  public example.Foo getFoo();
}

Question:  How did the ITDs in HasFooIntro make it into MyBarable?
I'm confused because the declare parents type expression of
HasFooIntro didn't match MyBarable, yet the HasFooIntro's
introductions are present in MyBarable.

Now, let's say that I want to allow the author of a class annotated
with @Barable to provide its own getFoo() method (required by HasFoo,
the superinterface of HasBar) with a new implementation that returns a
Goo:

public class Goo extends Foo {}

@Barable
public class MyGooable {
	public Foo getFoo() {
		return new Goo();
	}
}

Given this new class, when I call "javap example.MyGooable", I get the
following results:

Compiled from "MyGooable.java"

public class example.MyGooable
    extends java.lang.Object
    implements example.HasBar
{
  public boolean ajc$interField$example_HasFooIntro$example_HasFoo$returnNull;
  public example.MyGooable();
  public example.Foo getFoo();
  public boolean
ajc$interFieldGet$example_HasFooIntro$example_HasFoo$returnNull();
  public void ajc$interFieldSet$example_HasFooIntro$example_HasFoo$returnNull(boolean);
  public example.Bar getBar();
}

When I run the following class, I get the results shown below.

public class Main {

	public static void main(String[] args) {
		Foo foo = null;
		Bar bar = null;

		MyFooable fooable = new MyFooable();
		foo = fooable.getFoo();
		System.out.println("fooable.getFoo():" + foo + " "
				+ (foo == null ? "(no class)" : foo.getClass()));
		foo = fooable.getFoo();
		System.out.println("fooable.getFoo():" + foo + " "
				+ (foo == null ? "(no class)" : foo.getClass()));

		MyBarable barable = new MyBarable();
		foo = barable.getFoo();
		System.out.println("barable.getFoo():" + foo + " "
				+ (foo == null ? "(no class)" : foo.getClass()));
		foo = barable.getFoo();
		System.out.println("barable.getFoo():" + foo + " "
				+ (foo == null ? "(no class)" : foo.getClass()));
		bar = barable.getBar();
		System.out.println("barable.getBar():" + bar + " "
				+ (bar == null ? "(no class)" : bar.getClass()));

		MyGooable gooable = new MyGooable();
		foo = gooable.getFoo();
		System.out.println("gooable.getFoo():" + foo + " "
				+ (foo == null ? "(no class)" : foo.getClass()));
		foo = gooable.getFoo();
		System.out.println("gooable.getFoo():" + foo + " "
				+ (foo == null ? "(no class)" : foo.getClass()));
		bar = gooable.getBar();
		System.out.println("gooable.getBar():" + bar + " "
                                + (bar == null ? "(no class)" :
bar.getClass()));
	}
}

Results:

fooable.getFoo():example.Foo@de6ced class example.Foo
fooable.getFoo():null (no class)
barable.getFoo():example.Foo@1fb8ee3 class example.Foo
barable.getFoo():null (no class)
barable.getBar():null (no class)
gooable.getFoo():example.Goo@10b30a7 class example.Goo
gooable.getFoo():example.Goo@1a758cb class example.Goo
gooable.getBar():null (no class)

Thanks for your help, especially if you read this far! :)  I'd be
happy to zip up the example and send it if you're interested.

-matthew


Back to the top