0

私が経験している問題は次のとおりです。注釈を使用して収集される一連のクラスがあります。それらはすべて同じフォルダにあり、特定のアノテーションがある場合はReflections libraryでインスタンス化されます。これらのクラスはインスタンス化されていますが、静的なファクトリを呼び出す静的な初期化子があり、構造体を構築します。ファクトリで作成されたオブジェクトを取得しようとすると、JavaはInvocationTargetExceptionエラーをスローします。具体的には、ITEのスタックトレースを出力するときに、ファクトリにオブジェクトを要求する静的初期化子を直接指しています。Javaの `InvocationTargetException`は、リフレクションによるクラスのインスタンス化

以下は、問題を再現するために使用するコードです。私は、パッケージinference.rules内のクラスのいくつかの数にその注釈を適用InferenceRule.java

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface funRule { 
    String ruleName(); 
    String ruleType(); 
    String analyze() default "node"; 
} 

私は、アノテーションを持って

@InferenceRule(ruleName = "assign", ruleType = "term") 
public class Assign extends NodeAnalyzer { 
    public Assign() {super();} 
    public Assign(String... args) { super(args); } 
    public Rule gatherAllCOnstraints(InstructionNode node) { 
     // use the Identifier object here. 
    } 
    // rest of class here 
} 

NodeAnalyzerクラス、上記Assignクラスのスーパー:

public abstract class NodeAnalyzer { 
    protected Identifier identifier; 

    protected NodeAnalyzer() { 
     // Construct things here 
    } 

    protected NodeAnalyzer(String... args) { 
     // Construct other things here 
    } 

    // Construct common things here 
    { 
     this.identifier = IdentifierFactory.getIdentifier(); 
    } 
    // rest of class here 
} 

以下に説明するようにAssignクラスは、Inferenceクラスでインスタンス化されています

public class IdentifierFactory { 
    private static final Identifier identifier; 
    static { 
     if (ConfigFactory.getConfig().isDebEnabled()) { 
      identifier = new DBIdentifier(); 
     } else { 
      identifier = new NaiveIdentifier(); 
     } 
    } 

    public static Identifier getIdentifier() { 
     return identifier; 
    } 
} 

:あなたが見ることができるようにAssignNodeAnalyzerでインスタンス化パスから、

public class Inference { 
    public final String NODE_ANALYSIS = "NODE"; 
    public static final String INFERENCE_PACKAGE = "inference.rules"; 
    private final Map<String, NodeAnalyzer> nodeAnalyzer = new HashMap<>(); 
    private final Map<String, EdgeAnalyzer> edgeAnalyzer = new HashMap<>(); 
    public Inference() { 

    } 
    // other non-interesting things here 

    private void loadRules() { 
     Reflections reflection = new Reflections(INFERENCE_PACKAGE); 
     Set<Class<?>> annotated = reflection.getTypesAnnotatedWith(InferenceRule.class); 

     for(Class<?> clazz : annotated) { 
      try { 
       String name = clazz.getAnnotation(InferenceRule.class).ruleName(); 
       String type = clazz.getAnnotation(InferenceRule.class).ruleType(); 
       String analyze = clazz.getAnnotation(InferenceRule.class).analyze(); 
       if (StringUtils.equalsIgnoreCase(analyze, NODE_ANALYSIS)) { 
        final NodeAnalyzer newInstance = (NodeAnalyzer) clazz.getConstructor(InferenceType.class).newInstance(InferenceType.valueOf(type)); 
        this.nodeAnalyzer.put(name, newInstance); 
       } 
       // handle other cases... 
      } catch(InvocationTargetException ite) { 
       // For debugging, only 
       ite.printStackTrace(); 
       logger.error(ite.getCause.getMessage()); 
       logger.error(ite.getTargetException.getMessage()); 
      } 
     } 
    } 
} 

、それはIdentifierFactoryクラスを呼び出す必要がありますNaiveIdentifierクラス:

public class NaiveIdentifier { 
    private Set<Integer> unknowns = new HashSet<Integer>() {{ 
     unknowns.add(0); 
     // add more here. 
    }; 

    public NaiveIdentifier() {} // empty default constructor 
} 

ConfigFactoryクラスは、IdentifierFactoryクラスと同様のパターンに従います。特定の入力に基づいて設定を作成します。

java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 
    at phases.inference.Inference.loadRules(Inference.java:197) 
    at phases.inference.Inference.<init>(Inference.java:76) 
    at phases.PhaseFacade$PHASES.getPhase(PhaseFacade.java:27) 
    at phases.PhaseFacade.<init>(PhaseFacade.java:42) 
    at compilation.Compiler.runPhases(Compiler.java:126) 
    at compilation.Compiler.runAllOps(Compiler.java:118) 
    at Main.main(Main.java:45) 
Caused by: java.lang.ExceptionInInitializerError 
    at phases.inference.rules.NodeAnalyzer.<init>(NodeAnalyzer.java:35) 
    at phases.inference.rules.Assign.<init>(Assign.java:22) 
    ... 11 more 
Caused by: java.lang.NullPointerException 
    at typesystem.identification.NaiveIdentifier$1.<init>(NaiveIdentifier.java:23) 
    at typesystem.identification.NaiveIdentifier.<init>(NaiveIdentifier.java:22) 
    at typesystem.identification.IdentifierFactory.<clinit>(IdentifierFactory.java:25) 
    ... 13 more 

と::これらのことから

java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 
    at phases.inference.Inference.loadRules(Inference.java:197) 
    at phases.inference.Inference.<init>(Inference.java:76) 
    at phases.PhaseFacade$PHASES.getPhase(PhaseFacade.java:27) 
    at phases.PhaseFacade.<init>(PhaseFacade.java:42) 
    at compilation.Compiler.runPhases(Compiler.java:126) 
    at compilation.Compiler.runAllOps(Compiler.java:118) 
    at Main.main(Main.java:45) 
Caused by: java.lang.NoClassDefFoundError: Could not initialize class typesystem.identification.IdentifierFactory 
    at phases.inference.rules.NodeAnalyzer.<init>(NodeAnalyzer.java:35) 
    at phases.inference.rules.Assign.<init>(Assign.java:18) 
    ... 11 more 

、私は十分に根本的な原因が何であるかを見分けることはできませんスロー

正確な例外は次のようになります。これをさらに複雑にするために、私は他の入力ファイルを使ってこれを実行しようとしました。

public class NaiveIdentifier { 
    private Set<Integer> unknowns = new HashSet<Integer>() {{ 
     unknowns.add(0); 
     // add more here. 
    }}; // (<- added missing brace here) 

    public NaiveIdentifier() {} // empty default constructor 
} 

+0

を考慮することができる根本的な原因は、 'NaiveIdentifier'クラス(または多分匿名の内部クラス)のコンストラクタでスローNPEように見えます。質問を編集してこのクラスのコードを含めることができますか? 2番目のエラーメッセージは「クラスを初期化できませんでした...」は、既に静的初期化に失敗したクラスをロードしようとすると起こります。 Javaは、これらを初期化するための2回目の試みを気にしません。 –

+0

私は関連コードを追加しました。コードのすべての行をステップ実行した後、問題はNaiveIdentifierの 'HashSet'のインスタンス化によるものと思われました。それがなぜNPEを投げるのか理解できません。 – lilott8

+0

二重中括弧の初期化は反パターンです。実際に何が起こっているのかを隠しています(実際にインスタンス化したい型をサブクラス化する内部クラスのインスタンスを作成します)。 (要素); 'fieldName.add(要素);の代わりに'。皮肉なことに、 'unknowns.add(0);'と書かれているように、文字を保存するのではなく、内部クラスのコンストラクタから 'unknowns 'フィールドにアクセスしてコード全体を破壊する前に、構築されたオブジェクトがフィールドに割り当てられています。 – Holger

答えて

2

このコードは、「ダブルカーリーブレースの初期化」アンチパターンを使用しています。通常、このアンチパターンは、ソースコードにいくつかの入力を保存するために使用されます。

public class NaiveIdentifier { 
    private Set<Integer> unknowns = new HashSet<Integer>() {{ 
     // yeah, we saved writing the nine characters "unknowns." 
     add(0); 
     // add more here. 
    }}; 

    public NaiveIdentifier() {} // empty default constructor 
} 

コレクションクラスの新しいサブクラスを作成し、内部クラスは、その外側のクラスのインスタンスへの参照を保持するよう潜在的にメモリリークを作成するための犠牲にthis Q&Aで議論されているように、

厄介なことに、unknowns.の文字を省略していないので、このパターンの利点は得られませんでしたが、構築されたセットインスタンスで初期化されるフィールドにアクセスしているので、セットのコンストラクタ内から取得します。

public class NaiveIdentifier { 
    private Set<Integer> unknowns; 
    { 
     Set<Integer> temp = new HashSet<Integer>() {{ 
     unknowns.add(0); 
     // add more here. 
     }}; 
     unknowns = temp; 
    } 

    public NaiveIdentifier() {} // empty default constructor 
} 

このコードはNullPointerExceptionで失敗する理由、それは明らかに:他の言葉では、あなたのコードは次のコードと同等です。

これを修正するには、一貫してアンチパターンを使用します。つまり、unknowns.文字を削除して、上の例のように外部インスタンスのフィールドアクセスをスーパークラス呼び出しに変更します、あなたは簡単にアンチパターンなしできれいな初期化子を使用するようにコードを変更することができます:単一中括弧を使用している場合

public class NaiveIdentifier { 
    private Set<Integer> unknowns = new HashSet<Integer>(); 
    { 
     unknowns.add(0); 
     // add more here. 
    } 

    public NaiveIdentifier() {} // empty default constructor 
} 

、あなたはHashSetの内部クラスのサブクラスを作成するが、ちょうどになります初期化子を定義していませんNaiveIdentifierのコンストラクタに追加され、予想されるプログラムテキストの順序で実行されます。まず、初期化子unknowns = new HashSet<Integer>()、次にunknowns.add(…);ステートメント。簡単な初期設定ステートメントの場合

、あなたが代替

public class NaiveIdentifier { 
    private Set<Integer> unknowns = new HashSet<>(Arrays.asList(0, 1, 2, 3 …)); 

    public NaiveIdentifier() {} // empty default constructor 
} 
関連する問題