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

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

ただいまの
回答率

90.03%

getMethod、invokeの第2引数について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 5,379

XCUBE

score 93

こんなクラスがあったとします。

class Test {
    private Double  A;
    private Integer B;
    private String  C;

    public void setTest(Double  arg) { A=arg; }
    public void setTest(Integer arg) { B=arg; }
    public void setTest(String  arg) { C=arg; }
}

このクラスをリフレクションを使ってスマートに設定する関数を作ろうとしたのですが
getMethodの<<1>>、invokeの<<2>>の第2引数をどう書いていいのかわからず試行錯誤しております。
<<1>>はvalueを元にgetClass()を渡してみましたがNoSuchMethodExceptionのエラーとなります。

Test test = new Test();
SetFunc(test, new Double(123.45));
SetFunc(test, new Integer(123));
SetFunc(test, new String("ABC"));

public void SetFunc(Test cls, Object value) {
    Method method = cls.getClass().getMethod("setTest", <<1>>); 
    method.invoke(cls, <<2>>);
}

そもそもこのような場合はSetFuncのvalue引数のクラスによって振り分けるなど
しなければ実現できないのでしょうか?

ご教示の程、よろしくお願い致します。


追記

class Test {
    private float  D;
    private Double  A;
    private Integer B;
    private String  C;

    public void setTest(float  arg) { D=arg; }  // float型が引数
    public void setTest(Double  arg) { A=arg; }
    public void setTest(Integer arg) { B=arg; }
    public void setTest(String  arg) { C=arg; }
}
public class JavaApp {

    public static void main(String[] args) throws Exception {
        Test test = new Test();
        SetFunc(test, 123.4f);
        SetFunc(test, new Double(123.45d));
        SetFunc(test, new Integer(123));
        SetFunc(test, new String("ABC"));
    }

    public static void SetFunc(Test cls, Object value) throws Exception {        
        Method method = cls.getClass().getMethod("setTest", value.getClass());
        method.invoke(cls, value);
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

SetFunc メソッドがおかしいです。
以下のようにすればいけるのではないでしょうか?
あとメソッド名は小文字はじまりが基本ですので、setFunc にすべきですね。

public void SetFunc(Test cls, Object value) throws Exception {
    Method method = cls.getClass().getMethod("setTest", value.getClass());
    method.invoke(cls, value);
}

そもそも、これぐらいならコンストラクで設定するか、セッターで設定しても労力変わらない気がします。

// これも
SetFunc(test, new Double(123.45));
SetFunc(test, new Integer(123));
SetFunc(test, new String("ABC"));

// これも
new Test(new Double(123.45), new Integer(123), "ABC");

// これも労力同じでは?
test.setA(new Double(123.45));
test.setB(new Integer(123));
test.setC("ABC");

以下のような理由で、あまりリフレクションはおすすめしません。
・自分以外の人がコードを理解するのが難しい
・コンパイラでエラーを検出できない。全て実行時エラー
・IDEのリファクタリング機能が及ばない。例えば setTest メソッドの名前が変わったら終わり。
・デバッグがしづらく、バグの調査に苦労する
・多用しなければ問題ないレベルだが、通常のコードよりは数倍遅い

フレームワークを作る際には、リフレクションは必須ですが、
その場合、フレームワークの使用者からはカプセル化されてて意識することもないですからね。

追記

public class Main {
    public static void main(String[] args) throws Exception {
        Test test = new Test();
        SetFunc(test, new Double(123.45));
        SetFunc(test, new Integer(123));
        SetFunc(test, new String("ABC"));

        System.out.println(test);
    }

    public static void SetFunc(Test cls, Object value) throws Exception {
        Method method = cls.getClass().getMethod("setTest", value.getClass());
        method.invoke(cls, value);
    }
}

class Test {
    private Double A;
    private Integer B;
    private String C;

    public void setTest(Double arg) {
        A = arg;
    }

    public void setTest(Integer arg) {
        B = arg;
    }

    public void setTest(String arg) {
        C = arg;
    }

    @Override
    public String toString() {
        return new StringBuilder("A = ").append(A).append(System.lineSeparator())
                .append("B = ").append(B).append(System.lineSeparator())
                .append("C = ").append(C).append(System.lineSeparator())
                .toString();
    }
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/02/23 15:04

    具現化したコードまで添付いただきましてありがとうございます。
    確かに動きました!

    最初に私が投稿したTestクラスはビューアの方々に解りやすく説明するために
    最小限のものとして書いたもので、本当にお手間をお掛けして申し訳ないです。

    実際に試していたコードではクラスではなくfloat型を引数としたsetメソッド
    public void setTest(float arg)
    があり、これだとSetFuncでfloat型を渡してもObjectにキャストされて、
    getClass()の結果がjava.lang.FloatになるためNoSuchMethodExceptionと
    なったことが原因だとお蔭様で気づくことができました。

    このような場合 getMethodの引数としてfloat.classをObject valueから求めたり、
    invokeの引数としてfloat型に戻したり汎用的に使えるようにすることは難しい
    のでしょうか?

    キャンセル

  • 2016/02/23 15:27

    プリミティブ型を汎用的にするのは、たぶん無理だと思います。
    Javaのめんどくさいところです。。。

    過去に見たいくつかのフレームワークのソースも
    プリミティブの数だけの if と Object で分けて書かれていました。

    キャンセル

  • 2016/02/23 15:56 編集

    そうですか。これで諦めがつきました。

    データ型・・・いやプリミティブ型の種類も限られているので
    全てに対応することもそう大変ではなさそうということもハッキリしました。

    「プリミティブ型」という言葉も知り、大変勉強になりました。


    実際にやりたかったことはクラスの属性をgetMethods()によってgetメソッドを取得し、
    そこから得られた値を変更したものを、setメソッドで更新するための手段として
    考えていました。

    予めわかっているメソッドなので一つ一つgetとsetを書くのも悪くないのですが、
    数が多いのと引数の型がバラバラだったのでリフレクションでこじんまりと
    書けないかというところから深追いしておりました。

    ありがとうございました。

    キャンセル

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

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