0

現在、プロジェクトに AspectJ を適用していますが、少し奇妙な動作を見つけました。

Q1: 型間宣言を使用して現在のクラスに新しいコンストラクターを追加しましたが、新しいコンストラクターを使用してクラスをインスタンス化すると、クラスのメンバー変数が初期化されないことがわかりました。

例えば:

新しいコンストラクターを追加するクラス:

public class Child {

    public String name = "John";

    public Child(String desc) {
        // TODO Auto-generated constructor stub
    }
} 

アスペクトJコード:

public aspect MyTest {
    public Child.new(String desc, int num) {
        System.out.println("Child Name:" + this.name);
    }
}

Child を新しいコンストラクターでインスタンス化すると、次のようになります。

new Child("A child", 5)

メンバ変数this.nameは、元のコンストラクタで行われるように初期化されません。

しかし、元のコンストラクターを呼び出すと、次のようになります。

new Child("A child") 

メンバー変数this.nameは通常どおり「John」に初期化されます

結果:

子供の名前:null

これは AspectJ の制限ですか? とにかくこの問題を解決する方法はありますか?

メンバー変数の初期化のコードを新しいコンストラクターに追加したくありません。

Q2:新しく追加されたコンストラクターで、 super.method ()が正しく解決できないようです。

新しいコンストラクターを追加するクラス:

public class Child extends Parent{

    public String name = "John";

    public Child(String desc) {

    }
} 

ChildParentを拡張します。にはメソッドinit()があります

public class Parent {

    public void init() {
        //....
    }

}

アスペクトにChildの新しいコンストラクターを追加します。

public aspect MyTest {
    public Child.new(String desc, int num) {
        super.init();
    }
}

上記のアスペクト コードは例外をトリガーします。

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)

私の回避策は、クラスChildに別のメソッドを定義し、そのメソッド内で間接的に super.method() を呼び出すことです

たとえば、Childに対してsuper.init()を呼び出す新しいメソッドを追加します。

public void Child.initState()
{
    super.init();
}

これで、以下のように新しく追加されたコンストラクターで initState() を呼び出すことができます。

public aspect MyTest {
    public Child.new(String desc, int num) {
        this.initState();
    }
}

これは AspectJ の制限ですか? これがこの問題を解決する唯一の方法ですか?

お時間をいただきありがとうございました:)

4

3 に答える 3

2

最初の質問に対して、コンパイル時にリント警告が表示されるようです: (リント警告を閉じない限り)

「型間コンストラクターに明示的なコンストラクター呼び出しが含まれていません: ターゲット型のフィールド初期化子は実行されません [Xlint:noExplicitConstructorCall]」

したがって、それはAspectJ の制限だと思います。

これを行う最善の方法は、AspectJ によって追加されたコンストラクターでChildの他のコンストラクターを呼び出すことです。

例えば:

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);
    }
}
于 2013-07-18T08:43:15.253 に答える
0

ITD 紹介のコードは、クラスに直接追加するコードと同じです。したがって、導入したコンストラクターにメンバーの初期化コードがないと、もちろん、メンバーは初期化されないままになります。したがって、Q1 のコードを次のように変更する必要があります。

public Child.new(String name, int age) {
    this.name = name;
    this.age = age;
    System.out.println("Child Name:" + this.name);
}

Q2に関しては、私にとってはうまくいきます。

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();
    }
}

プリント:

P.init
C.init

導入されたメソッドを動作以外のものに変更することもinitできます (コードに一致させるため)。

あなたのコメントについて: Q1 でどのような違いがあったかわかりません。わかりません。

あなたのコメントのQ2の部分については、コンストラクターの配置がうまくいきます:

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);
    }
}

版画this.name: myname this.age: 2

于 2013-07-15T17:54:12.280 に答える