Reputation: 514
I am currently applying AspectJ to our project, and I found a behavior which is a bit strange to me.
Q1: I added a new constructor to my current class with inter-type declaration, and found that the class's member variable is not initialized if the new constructor is used to instantiate my class.
For example:
The class which I'll add a new constructor to:
public class Child {
public String name = "John";
public Child(String desc) {
// TODO Auto-generated constructor stub
}
}
The aspectJ code:
public aspect MyTest {
public Child.new(String desc, int num) {
System.out.println("Child Name:" + this.name);
}
}
If I instantiate the Child with the new constructor:
new Child("A child", 5)
the member variable this.name is not initialized as will be done with the original constructor.
But, if I call the original constructor:
new Child("A child")
the member variable this.name will be initialized to "John" as usual
The result:
Child Name:null
Is this a limitation of AspectJ? Is there anyway to resolve this issue?
I don't really want to add the code for member variable initialization to the new constructor.
Q2: It seems in the newly added constructor, super.method() can not be correctly resolved.
The class which I'll add a new constructor to:
public class Child extends Parent{
public String name = "John";
public Child(String desc) {
}
}
Child extends Parent. Parent has a method init()
public class Parent {
public void init() {
//....
}
}
I add a new constructor for the Child in my aspect.
public aspect MyTest {
public Child.new(String desc, int num) {
super.init();
}
}
The above aspect code will trigger an exception.
Exception in thread "main" java.lang.NoSuchMethodError: com.test2.Child.ajc$superDispatch$com_test2_Child$init()V
at MyTest.ajc$postInterConstructor$MyTest$com_test2_Child(MyTest.aj:19)
at com.test2.Child.<init>(Child.java:1)
at MainProgram.main(MainProgram.java:11)
My workaround is to define another method for my class Child, and indirectly call the super.method() within that method
For example, add a new method that calls super.init() for Child
public void Child.initState()
{
super.init();
}
Now, I can call initState() in the newly added constructor like below:
public aspect MyTest {
public Child.new(String desc, int num) {
this.initState();
}
}
Is this a limitation of AspectJ? Is this the only way to resolve this issue?
Thank you all for your time :)
Upvotes: 0
Views: 616
Reputation: 26
For the second question, I think it's a bug of aspectJ. That decompile the woven target byte code will find that the method “com.test2.Child.ajc$superDispatch$com_test2_Child$init()V” will be inserted. It implies this method should be generate by aspectJ, but there is no such method in the byte code.
Upvotes: 1
Reputation: 31
Foe the first questions, it seems that the lint warning will appear when compiling: (unless you close the lint warning)
"inter-type constructor does not contain explicit constructor call: field initializers in the target type will not be executed [Xlint:noExplicitConstructorCall]"
Therefore I'd say it's an AspectJ's limitation.
The best way to do this might be call the other constructors of Child in the constructor added by AspectJ
For example:
public aspect MyTest {
public Child.new(String desc, int num) {
this("Hello"); // -> This will call the constructor of Child, and trigger fields initialization
System.out.println("Child Name:" + this.name);
}
}
Upvotes: 2
Reputation: 1284
The code for an ITD introduction is no different that the code that you would add to a class directly. So without member initialization code in your introduced constructor, members will , of course, remain uninitialized. So you need to change you code in Q1 as follows.
public Child.new(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Child Name:" + this.name);
}
As for Q2, it works fine for me.
class Parent {
public void init() {
System.out.println("P.init");
}
}
class Child extends Parent {
}
aspect Intro {
public void Child.init(){
super.init();
System.out.println("C.init");
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.init();
}
}
prints:
P.init
C.init
Changing the introduced method to something other than init
works too (to match your code).
Regarding your comment: I fail to see what difference you have made in Q1. Sorry, I don't get it.
As for Q2 part of your comment, constructor arrangement works for me:
class Parent {
protected String name;
public Parent(String name) {
this.name = name;
}
}
class Child extends Parent {
int age;
public Child(String name) {
super(name);
}
}
aspect Intro {
public Child.new(String name, int age){
super(name);
this.age = age;
System.out.println("this.name: " + this.name + " this.age: " + this.age);
}
}
prints this.name: myname this.age: 2
Upvotes: 0