質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.58%

リフレクション コンストラクタが取得できない「続」

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,430

tekondo

score 26

 前提・実現したいこと 

前回の継続の質問です。
リフレクションを用いて動的にクラスを作成したいのですがうまく行きません。
前回の質問で色々試行錯誤したことをまとめてこちらの質問に改めて投げます。

package enshud.s2.parser;
import java.lang.reflect.Constructor;

public class Parser {
    public static void main(final String[] args) {
        new Parser().run("data/ts/normal01.ts");
    }

    public void run(final String inputFileName) {
           make_class();
        }

    void make_class(){       
        try {        
        Class<?> testcl = test.class;
        Constructor<?> cons = testcl.getDeclaredConstructor(String.class);
        cons.newInstance("ss");
        } catch(ReflectiveOperationException e) {
            e.printStackTrace();
        }    
    }

    public class test {
        public test(String a){            
        }
    }
}


このプログラムを実行すると、make_class関数でtestcl.getDeclaredConstructorを実行した時にエラーが出ます。

 エラーメッセージ

java.lang.NoSuchMethodException: enshud.s2.parser.Parser$test.<init>(java.lang.String)


これは何故なのでしょうか?
色々試して見た結果、

    public static class test {
        public test(String a){            
        }
    }


とstaticをつけるとgetDeclaredConstructorはパスすることができました。しかしstaticをつけてしまうと不都合が生じるためstaticはつけたくないです。何かわかる方いらっしゃいますでしょうか?

 補足情報(FW/ツールのバージョンなど)

java -JDK8

参考URL:http://www.ne.jp/asahi/hishidama/home/tech/java/reflection.html

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+4

staticでない内部クラスのインスタンスを生成する際にはそのクラスを囲むクラスのインスタンスが生成時に必要になります。

public class Outer {
  public class Inner {
  }
}

...

// construction
import path_to_package.Outer;
import path_to_package.Outer.Inner;

class T {
  public static void main(String[] args) {
    Outer outer = new Outer();
    Inner inner = outer.new Inner();
  }
}


内部的には上記のouterはInnerのコンストラクターの第一引数として暗黙的に仮定されるようになっています。つまり上記の例ではInnerのコンストラクターは
Inner()
ではなくて
Inner(Outer implicitOuter)
となるのです。これを前提としてgetDeclaredConstructorの引数に暗黙的な引数を追加し、Innerの生成時に外側のクラスのインスタンスを渡すと期待通りに動きます。

例:
a/Test.java

package a;

import b.AllExp;
import b.Outer;
import java.lang.reflect.Constructor;

class Test {
  public static void main(String[] args) {
    System.out.println(Test.class.getClassLoader());
    System.out.println(AllExp.class.getClassLoader());
    Outer outer = new Outer();
    makeInstance(null, outer, "ProgramToken");
  }

  static void makeInstance(AllExp parent, Outer outer, String className) {
    className = "b.Outer$" + className;
    try {
      Class<?> clazz = Class.forName(className);
      Constructor<?> ctor = clazz.getDeclaredConstructor(
        Outer.class, AllExp.class);
      ctor.setAccessible(true);
      Object inst = ctor.newInstance(outer, parent);
      System.out.println("class of instance is "
        + inst.getClass().getCanonicalName());
    } catch (ReflectiveOperationException e) {
      throw new RuntimeException(e);
    }
  }
}


b/AllExp.java

package b;

public class AllExp {
  AllExp parent;

  AllExp(AllExp parent) {
    this.parent = parent;
  }
}


b/Outer.java

package b;

public class Outer {
  class ProgramToken extends AllExp {
    ProgramToken(AllExp parent) {
      super(parent);
    }
  }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/03 11:49

    回答を参考に作成したところ実装することができました!!!
    内部クラスのコンストラクタを作成する場合は囲んでいるクラスのインスタンスを指定する必要があるんですね。とても勉強になりました!ありがとうございます!!!

    キャンセル

  • 2018/11/03 14:00

    回答では省きましたが、リフレクションを用いるならある程度深いJava言語/JVMの実装面の知識が必要です。最も正確・詳細な情報源はJVM/Java言語仕様書ということになりますがこれらは分量が膨大かつ専門性が高いので誰にとって有効とは限りません。そこで一案としてjavapを使い実際のコンパイル結果(クラスファイルの中身)を観察してみることをお勧めします。そうすれば最初に挙げた例のInnerのコンストラクターが以下のように表示されるので
     public b.Outer$Inner(b.Outer);
      descriptor: (Lb/Outer;)V
    暗黙的な引数を要求することだけはすぐにわかります。
    このように「事実を調べる方法」を知っておくと「どうしたらよいか」「なぜそうなっているか」を推測・調査するための手掛かりになります。
    ちなみにgetDeclaredConstructorsで全てのコンストラクターを列挙し引数を調べるなんて方法も取れますがjavapを使ったほうがずっと簡単ですね :-D

    キャンセル

0

実際の答えにはなっていませんが、気になるところを上げると
・クラスtestはめんどくさくても別ファイルにしたらいいと思います。こういうのを内部クラス、というのですが、例では内部クラスを使用していないので、やってみたらどうでしょう。別ファイルに分けると、クラスにstaticつけるとエラーだった気がするので、

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/03 08:39

    ご回答ありがとうございます!!!

    > 例では内部クラスを使用していないので、やってみたらどうでしょう

    ここでの例とはなんのことでしょうか?

    キャンセル

  • 2018/11/03 08:41

    例はあのサイトのことです。

    キャンセル

  • 2018/11/03 11:49

    無事解決できました!ありがとうございます!!!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.58%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る