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

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

ただいまの
回答率

88.58%

リフレクション コンストラクタが作成できない

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,343

tekondo

score 26

 前提・実現したいこと

動的にクラスからインスタンスを作りたいです。
そのためにリフレクション機能を使おうと思っているのですが、うまく行きません。
forname()を用いて文字列からクラスを作成後、コンストラクタをconstractorを用いて作成しようとする段階でerrorが起きてしまいます。

何が原因かわかる方いらっしゃいますでしょうか?

 発生している問題・エラーメッセージ

java.lang.NoSuchMethodException: enshud.s2.parser.Parser$ProgramNameToken.<init>(enshud.s2.parser.Parser$AllExp)
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getConstructor(Class.java:1825)

 該当のソースコード

実行しているメソッド

void make_class(AllExp parent,String strClass){    
    // リフレクション
        strClass = "enshud.s2.parser.Parser$" + strClass;
        try {
            // クラスの取得
            Class<?> class_name = Class.forName(strClass);
            // インスタンスの生成
            Constructor constructor = class_name.getConstructor(AllExp.class);
                 //ここでエラー発生
            constructor.newInstance(parent);    
        } catch(ReflectiveOperationException e) {
            e.printStackTrace();
        }    

}


作成したいインスタンスのクラスは以下のようになっています。

#内容修正後

    public class ProgramNameToken extends AllExp{//program名
        public ProgramNameToken(AllExp parent_exp) {
            super(parent_exp);
        }
        public AllExp exp_rule() {
            new NameToken(this);
            return this;
        }
    }

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

java -JDK8

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

 注意

この質問は
リンク内容に引き継ぎました。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • euledge

    2018/10/29 20:44

    エラーメッセージでは、ProgramNameTokenとなっていますがクラス名はProgramTokenとなっているのはなぜ?

    キャンセル

  • tekondo

    2018/10/29 23:03

    間違えて違うクラスを表示していました、ご指摘ありがとうございます🙇‍♂️

    キャンセル

回答 3

checkベストアンサー

+2

エラーの原因は他の方の回答どおりと思います。

コンストラクタをconstractorを用いて作成しようとする段階でerror

正確にはgetConstructorでエラーが発生したというべきですね。

 対処1

リフレクションにより操作する対象のクラスのメンバー(フィールドやメソッドやコンストラクターなど)を全てpublicにできるならそれが一番簡単な対処になります。

 対処2

以下のようにすれば他パッケージのクラスの非publicなコンストラクターを呼び出すことができます。(無制限にできるというわけではなくSecurityExceptionが発生しないことが前提ですが)

  • getConstructorの代わりにgetDeclaredConstructorを使う。
    このメソッドでpublicでないコンストラクターも参照できます。
  • setAccessibleでメンバー(本件の場合コンストラクター)のアクセス制限を外す
    getDeclaredConstructorでpublicでないコンストラクターも参照できますが、そのままでは通常のアクセスガードが適用されるためnewIntanceを呼び出せません。ガードを特別に解除したい場合にはこのメソッドを使います。

例:

Test.java

package a;

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

class Test {
  public static void main(String[] args) {
    makeInstance(null, "ProgramToken");
  }

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

AllExp.java

package b;

public class AllExp {
  AllExp parent;

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

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

案1のようにコンストラクターをpublicにする方法では対象クラスが他のパッケージから好きに生成できるようになります。しかし「パッケージプライベートのままにしておきたい」という事情がある場合には案2が役立つと思います。


補記:
1) 回答例はJava10で確認しました。Java8でも動くつもりで書いたのですが何か手落ちがあったらスミマセン。
2) ちなみにJava10Java9でmoduleが導入されたのでたとえpublicであっても他のモジュールからのアクセスをガードできるようになりました。ですのでJava10以降を使うなら対処1がより適用しやすいものになると思います。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/10/29 23:07

    アドバイスありがとうございます。
    超わかりやすいです、、、
    コンストラクタをpublicに変更してみて再実行したのですが、エラーが変わらず、、、
    どこか勘違いしている箇所ありますでしょうか?
    アドバイスの中で一つ気になっている点があります。

    > 対象のクラスのメンバー(フィールドやメソッドやコンストラクターなど)を全てpublicにできるなら

    とのことなのですが、コンストラクタ以外のメソッド等も全てpublicにしなければいけないのでしょうか?

    キャンセル

  • 2018/10/29 23:19 編集

    > コンストラクター以外のメソッドも全てpublic
    いえ、リフレクションを用いてアクセスするメンバーだけでいいです。
    > どこか勘違いしている個所
    そういう場合、再現する小さなコードを示すのが常道です。具体的な事実があればすぐにわかることでも曖昧な情報だけだとすぐにわからないのがプログラミングです。

    ところで、よくスタックトレースを見るとProgramNameTokenになってますね。ソースコード上にはProgramTokenクラスの定義しか書かれてませんが、ProgramNameTokenの定義はどうなっているでしょうか?実はそちらのコンストラクターをpublicにしてなかったりして?

    キャンセル

  • 2018/11/03 07:53

    ご回答ありがとうございました。まさにおっしゃる通りの内容ばかりでした。
    public を確認して試して見たのですが、思うような結果が取得できず未だ苦戦中です。
    言っていただいたことを踏まえて別の質問に改めて投げようと思います。
    回答していただいてありがとうございました。

    キャンセル

+1

Class#getConstructor

このClassオブジェクトが表すクラスの指定されたpublicコンストラクタをリフレクトするConstructorオブジェクトを返します。(以下略)

コンストラクタがpublicではないからでは?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/10/29 23:04

    アドバイスありがとうございます。
    コンストラクタをpublicに変更してみて再実行したのですが、エラーが変わらず、、、
    どこか勘違いしている箇所ありますでしょうか?

    キャンセル

+1

参考URLgetConstructor(引数の型, …)の説明に

引数ありコンストラクターを返す。(引数の種類が合致するpublicなもの)

とあります。ProgramTokenのコンストラクタをpublicにしたら解決できるかもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/10/29 23:05

    アドバイスありがとうございます。
    コンストラクタをpublicに変更してみて再実行したのですが、エラーが変わらず、、、
    どこか勘違いしている箇所ありますでしょうか?

    キャンセル

  • 2018/10/29 23:37

    申し訳ないですが、私にはこれ以上の回答は難しいです。お力になれずにすみません。
    一応確認ですが、コードの難読・圧縮化ツールの類は使用していないでしょうか?過去に私がAndroidアプリを開発していたときに、Proguardというコード難読化ツールが、ビルド時にメソッド名などを変更していたため、メソッド名を参照して動くコードが正常に動作しないことがありました。

    キャンセル

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

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

関連した質問

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