みなさん、 こんにちは。私は「Have your cake and eat it too: Meta-programming in Java」というプレゼンテーションを見ていました。
プレゼンターは、Tapestry の著者の 1 人である Howard M. Lewis Ship でした。これを作成するにあたり、「plastic」と呼ばれるサブプロジェクトが作成され、ASM を利用してバイトコードを変更しました。
私は専門家のふりをするつもりはありませんが、最終的には、注釈付きのクラス、メソッド、およびフィールドを使用してさらに Java コードを生成し、ボイラープレート コードを削減できるようなコードを記述できるようになるはずです。
私の質問 以下のコードは、私の問題を示す完全な例です。テスト例では、equals() および hashCode() の実装が含まれるように EqualsDemo クラスを変更する必要があります。それを実行すると、基本的に「com.example.plastic.transformed.EqualsDemo」タイプのオブジェクトを「com.example.plastic.transformed.EqualsDemo」(はい、同じクラス) にキャストできないというエラーが表示されます。 .
プレゼンターは、これらのエラーがどこから発生したかをほのめかさずに迷惑だと述べました.これまでの検索では、それらが異なるクラスローダーに関係していることを示しています. しかし、私は問題を完全に解決できなかったので、ここで質問します(!)
com.example.plastic.transformed.EqualsDemo cannot be cast to com.example.plastic.transformed.EqualsDemo
at MainClass.main(MainClass.java:28)
それで、私は何をする必要がありますか?クラスローダーを置き換えますか? (もしそうなら、どうやって?) または私が得られないプラスチックの一部はありますか? 物事をスムーズに進めるために使用する必要があるプロキシオブジェクトなどを生成するための方法はありますか?
PS!これまでに見つけた例はすべて、注釈付きインスタンスの最終的な使用において、Groovyと思われるものを使用しています。
うまくいけば、誰かが私よりも有能です:)
リンク: Tapestry のホームページ (プラスチックはダウンロードに jar として含まれています): http://tapestry.apache.org/
Main.java
import java.util.ArrayList;
import java.util.List;
import org.apache.tapestry5.internal.plastic.StandardDelegate;
import org.apache.tapestry5.plastic.ClassInstantiator;
import org.apache.tapestry5.plastic.PlasticManager;
import com.example.plastic.transformer.EqualsHashCodeTransformer;
import com.example.plastic.transformed.EqualsDemo;
public class MainClass {
public static void main(String[] args) {
List<String> pList = new ArrayList<String>();
pList.add("com.example.plastic.transformed");
PlasticManager pm = PlasticManager
.withContextClassLoader()
.delegate( new StandardDelegate(new EqualsHashCodeTransformer()) )
.packages(pList)
.create();
final String EQUALSDEMO = "com.example.plastic.transformed.EqualsDemo";
ClassInstantiator<EqualsDemo> i = pm.getClassInstantiator(EQUALSDEMO);
i.newInstance().hashCode();
/*
com.example.plastic.transformed.EqualsDemo cannot be cast to com.example.plastic.transformed.EqualsDemo
at MainClass.main(MainClass.java:28)
*/
}
}
ImplementEqualsHashCode.java
package com.example.plastic.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ImplementEqualsHashCode {
}
EqualsHashCodeTransformer.java
package com.example.plastic.transformer;
import java.util.ArrayList;
import java.util.List;
import org.apache.tapestry5.plastic.FieldHandle;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodDescription;
import org.apache.tapestry5.plastic.MethodInvocation;
import org.apache.tapestry5.plastic.PlasticClass;
import org.apache.tapestry5.plastic.PlasticClassTransformer;
import org.apache.tapestry5.plastic.PlasticField;
import com.example.plastic.annotations.*;
public class EqualsHashCodeTransformer implements PlasticClassTransformer {
private MethodDescription EQUALS = new MethodDescription("boolean", "equals", "java.lang.Object");
private MethodDescription HASHCODE = new MethodDescription("int", "hashCode");
private static final int PRIME = 37;
public void transform(PlasticClass plasticClass){
//check that the class is annotated
if(!plasticClass.hasAnnotation(ImplementEqualsHashCode.class)) {
return;
}
List<PlasticField> fields = plasticClass.getAllFields();
final List<FieldHandle> handles = new ArrayList<FieldHandle>();
for(PlasticField field : fields){
handles.add(field.getHandle());
}
//HashCode method introduction :)
plasticClass.introduceMethod(HASHCODE).addAdvice(new MethodAdvice() {
public void advise(MethodInvocation invocation){
Object instance = invocation.getInstance();
int result = 1;
for(FieldHandle handle : handles){
Object fieldValue = handle.get(instance);
if(fieldValue != null)
result = (result * PRIME) + fieldValue.hashCode();
}
invocation.setReturnValue(result);
//Don't proceed to the empty introduced method
}
});
plasticClass.introduceMethod(EQUALS).addAdvice(new MethodAdvice() {
public void advise(MethodInvocation invocation) {
Object thisInstance = invocation.getInstance();
Object otherInstance = invocation.getParameter(0);
invocation.setReturnValue(isEqual(thisInstance, otherInstance));
//Don't proceed to the empty introduced method
}
private boolean isEqual(Object thisInstance, Object otherInstance) {
if(thisInstance == otherInstance)
return true;
if(otherInstance == null)
return false;
if(!(thisInstance.getClass() == otherInstance.getClass()))
return false;
for(FieldHandle handle : handles){
Object thisValue = handle.get(thisInstance);
Object otherValue = handle.get(otherInstance);
if(!(thisValue == otherValue || thisValue.equals(otherValue)))
return false;
}
return true;
}
});
}
}
EqualsDemo.java
package com.example.plastic.transformed;
import com.example.plastic.annotations.ImplementEqualsHashCode;
@ImplementEqualsHashCode
public class EqualsDemo {
private int intValue;
private String stringValue;
public int getIntValue(){
return intValue;
}
public void setIntValue(int intValue){
this.intValue = intValue;
}
public String getStringValue(){
return stringValue;
}
public void setStringValue(String stringValue){
this.stringValue = stringValue;
}
}