java:あるタイプから別のタイプへの変数の動的キャストを行うにはどうすればよいですか?


85

Java変数の動的キャストを実行したいのですが、キャストタイプは別の変数に格納されています。

これは通常のキャストです:

 String a = (String) 5;

これは私が欲しいものです:

 String theType = 'String';
 String a = (theType) 5;

これは可能ですか?もしそうなら、どのように?ありがとう!

更新

HashMap受け取ったものをクラスに入力しようとしています。

これはコンストラクターです:

public ConnectParams(HashMap<String,Object> obj) {

    for (Map.Entry<String, Object> entry : obj.entrySet()) {
        try {
            Field f =  this.getClass().getField(entry.getKey());                
            f.set(this, entry.getValue()); /* <= CASTING PROBLEM */
        } catch (NoSuchFieldException ex) {
            log.error("did not find field '" + entry.getKey() + '"');
        } catch (IllegalAccessException ex) {
            log.error(ex.getMessage());         
        }
    }

}

ここでの問題は、クラスの変数の一部が型Doubleであり、数値3を受け取った場合、それをIntegerと見なし、型の問題が発生することです。


それは意味がありません。変数名を文字列を文字列にキャストするタイプにしたいですか?何?
cletus 2010年

3
答えはわかりませんが、これはメンテナンスの地獄になるのではないかと心配しています... Javaを自分で学ぶだけですが、このようなアプローチが必要な状況は避けたいと思います。私はあなたがすることは何でもより良い方法で実行できるとかなり確信しています...ちょうど私の2セント。
セヤヌス2010年

わかりました。私が達成しようとしていることについて、より多くの情報を提供します。
ufk 2010年

以下の私の答えも更新しました!
user85421 2010年

回答:


14

あなたの更新については、Javaでこれを解決する唯一の方法は、コードを書くことになるとたくさんのカバーすべての例ifelseしてinstanceof表現。あなたがやろうとしていることは、動的言語でプログラミングするのに使われているように見えます。静的言語では、あなたがやろうとしていることはほとんど不可能であり、おそらくあなたがやろうとしていることに対して全く異なるアプローチを選ぶでしょう。静的言語は動的言語ほど柔軟ではありません:)

Javaのベストプラクティスの良い例ですBalusCによって答え(つまりObjectConverter)とAndreas_Dによって答え(つまりAdapter下)。


それは意味がありません

String a = (theType) 5;

の型aは静的にバインドされているStringため、この静的型に動的にキャストしても意味がありません。

PS: 例の最初の行は次のように書くことができますClass<String> stringClass = String.class;が、それでもstringClass変数のキャストには使用できません。


私が投稿した更新が私がやろうとしていることを説明することを願っています。私はPHPのバックグラウンドを持っているので、Javaではこのことを実現できないかもしれません。
ufk 2010年

正確に言えば、Javaではそれほど動的にすることはできません。私のアップデートも参照してください。
akuhn 2010年

、これはあなたが行かなければならない先の長さ(痛み)です...以下を参照BalusCの答え
akuhn

私はこれが遅いことを知っていますが、彼は[thetype] a =(thetype)some_object;を意味したと思います。無意味であるあなただけ= some_objectの細かいオブジェクトんすることができますので、
ジョージ・ザビエル

120

はい、Reflectionを使用して可能です

Object something = "something";
String theType = "java.lang.String";
Class<?> theClass = Class.forName(theType);
Object obj = theClass.cast(something);

ただし、結果のオブジェクトはObject型の変数に保存する必要があるため、これはあまり意味がありません。特定のクラスの変数が必要な場合は、そのクラスにキャストできます。

特定のクラスを取得する場合Number、次に例を示します。

Object something = new Integer(123);
String theType = "java.lang.Number";
Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class);
Number obj = theClass.cast(something);

しかし、それを行う意味はまだありませんNumber。にキャストするだけです。

オブジェクトをキャストしても何も変わりません。これは、コンパイラがそれを処理する方法にすぎません。
そのようなことをする唯一の理由は、オブジェクトが指定されたクラスのインスタンスであるか、そのサブクラスのインスタンスであるかを確認することですが、それはinstanceofまたはを使用して行う方がよいでしょうClass.isInstance()

更新

あなたの最後の更新によると、本当の問題はあなたがに割り当てられるべきIntegerあなたの中にHashMapあるということDoubleです。この場合にできることは、フィールドのタイプを確認し、次のxxxValue()メソッドを使用することです。Number

...
Field f =  this.getClass().getField(entry.getKey());
Object value = entry.getValue();
if (Integer.class.isAssignableFrom(f.getType())) {
    value = Integer.valueOf(((Number) entry.getValue()).intValue());
} else if (Double.class.isAssignableFrom(f.getType())) {
    value = Double.valueOf(((Number) entry.getValue()).doubleValue());
} // other cases as needed (Long, Float, ...)
f.set(this, value);
...

(私が間違ったタイプを持っているという考えが好きかどうかはわかりませんMap


22

あなたはObjectConverterこれのためにある種を書く必要があるでしょう。これは、変換するオブジェクトがあり、変換先のターゲットクラスがわかっている場合に実行できます。この特定のケースでは、によってターゲットクラスを取得できますField#getDeclaringClass()

あなたはここでそのような例を見つけることができますObjectConverter。それはあなたに基本的な考えを与えるはずです。より多くの変換の可能性が必要な場合は、目的の引数と戻り値の型を使用してメソッドを追加するだけです。


@ BalusC-ObjectConverterコードは興味深いと思いますが、その使用例を説明していただけますか?
srini.venigalla 2010年

設定より規約が優先され、ソースタイプがターゲットタイプと一致しない場合に役立ちます。私は2〜3年前に、(趣味の目的で)ORMおよびMVCフレームワークで使用しました。ブログ記事の先頭のテキストも参照してください。
BalusC 2010年

12

これClass.cast()は、指定されたパラメーターを所有しているクラスインスタンスのタイプに動的にキャストするメソッドを使用して実行できます。特定のフィールドのクラスインスタンスを取得するgetType()には、問題のフィールドのメソッドを使用します。以下に例を示しますが、すべてのエラー処理が省略されているため、変更せずに使用しないでください。

public class Test {

    public String var1;
    public Integer var2;
}

public class Main {

    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("var1", "test");
        map.put("var2", 1);

        Test t = new Test();

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Field f = Test.class.getField(entry.getKey());

            f.set(t, f.getType().cast(entry.getValue()));
        }

        System.out.println(t.var1);
        System.out.println(t.var2);
    }
}

1
エントリタイプがフィールドタイプのスーパータイプではない場合はどうなりますか?次に、プログラムで変換する必要があります。
BalusC 2010年

7

以下のような簡単なcastMethodを書くことができます。

private <T> T castObject(Class<T> clazz, Object object) {
  return (T) object;
}

あなたの方法では、次のように使用する必要があります

public ConnectParams(HashMap<String,Object> object) {

for (Map.Entry<String, Object> entry : object.entrySet()) {
    try {
        Field f =  this.getClass().getField(entry.getKey());                
        f.set(this, castObject(entry.getValue().getClass(), entry.getValue()); /* <= CASTING PROBLEM */
    } catch (NoSuchFieldException ex) {
        log.error("did not find field '" + entry.getKey() + '"');
    } catch (IllegalAccessException ex) {    
        log.error(ex.getMessage());          
    }    
}

}

はい、私はこの答えが質問者が望んでいるものだと思います。彼/彼女はクラス<?>を取得し、インスタンスをクラス「?」に変換したいと考えています。デフォルトでは、Javaはこれをサポートしていません。しかし、<T>を使用してこれを行うことができます。
ruiruige19 9118

@ ruiruige1991これは間違っています。この場合のTはジェネリックです。ジェネリックは実行時に何もしません。(T)blahは、型消去のため、実行時に(Object)blahになります。要するに、ジェネリックス->コンパイル時であり、実行時に効果がありません。動的->ランタイムおよびジェネリック->コンパイル時なので、ジェネリックは役に立ちません。
ジョージザビエル

5

それは機能し、あなたのアプローチには共通のパターンさえあります:アダプターパターン。ただし、もちろん、(1)Javaプリミティブをオブジェクトにキャストする場合は機能せず、(2)クラスは適応可能である必要があります(通常はカスタムインターフェイスを実装することにより)。

このパターンを使用すると、次のようなことができます。

Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);

そして、WolfクラスのgetAdapterメソッド:

public Object getAdapter(Class clazz) {
  if (clazz.equals(Sheep.class)) {
    // return a Sheep implementation
    return getWolfDressedAsSheep(this);
  }

  if (clazz.equals(String.class)) {
    // return a String
    return this.getName();
  }

  return null; // not adaptable
}

あなたにとって特別なアイデア-それは不可能です。キャストに文字列値を使用することはできません。


2

あなたの問題は「ダイナミックキャスティング」の欠如ではありません。キャストIntegerDoubleすべてのことはできません。あなたはJavaに1つのタイプのオブジェクト、おそらく互換性のないタイプのフィールドを与え、どういうわけか自動的にタイプ間の変換方法を理解させたいようです。

この種のことは、JavaやIMOのような強く型付けされた言語に対する嫌悪感です。これには非常に理由があります。

あなたは実際に何をしようとしていますか?反射の使用はすべてかなり怪しいように見えます。


@name:あなたが提案し続ける編集について:私はプリミティブ値についてではなく、ラッパークラス(大文字とスタイルをコードとして示す)について話していることに注意してください。それらの間でキャストすることは絶対に不可能です。
Michael Borgwardt 2013

1

これをしないでください。代わりに、適切にパラメーター化されたコンストラクターを用意してください。とにかく接続パラメータのセットとタイプは固定されているので、これをすべて動的に行う意味はありません。


1

ほとんどのスクリプト言語(Perlなど)と非静的コンパイル時言語(Pickなど)は、自動実行時動的文字列から(比較的任意の)オブジェクトへの変換をサポートしています。これは、型の安全性を失うことなくJavaでも実現でき、静的に型付けされた言語は、動的キャストで悪事を行う他の言語の厄介な副作用なしに提供します。疑わしい計算を行うPerlの例:

print ++($foo = '99');  # prints '100'
print ++($foo = 'a0');  # prints 'a1'

Javaでは、これは私が「クロスキャスティング」と呼ぶメソッドを使用することでよりよく達成されます(IMHO)。クロスキャスティングでは、リフレクションは、次の静的メソッドを介して動的に検出されるコンストラクターとメソッドの遅延ロードされたキャッシュで使用されます。

Object fromString (String value, Class targetClass)

残念ながら、Class.cast()などの組み込みJavaメソッドは、StringからBigDecimal、StringからInteger、またはサポートするクラス階層がないその他の変換に対してこれを実行しません。私の側では、これを達成するための完全に動的な方法を提供することが重要です-事前の参照が正しいアプローチであるとは思わない-すべての変換をコーディングする必要があります。簡単に言えば、実装は、合法/可能であれば、文字列からキャストするだけです。

したがって、解決策は、次のいずれかのパブリックメンバーを探す単純なリフレクションです。

STRING_CLASS_ARRAY =(new Class [] {String.class});

a)メンバーmember = targetClass.getMethod(method.getName()、STRING_CLASS_ARRAY); b)メンバーmember = targetClass.getConstructor(STRING_CLASS_ARRAY);

すべてのプリミティブ(Integer、Longなど)とすべての基本(BigInteger、BigDecimalなど)、さらにはjava.regex.Patternもすべてこのアプローチでカバーされていることがわかります。私はこれを使用して、より厳密なチェックが必要な任意の文字列値の入力が大量にある本番プロジェクトで大きな成功を収めました。このアプローチでは、メソッドがない場合、またはメソッドが呼び出されたときに例外がスローされます(BigDecimalへの非数値入力やパターンの不正な正規表現などの不正な値であるため)。これにより、に固有のチェックが提供されます。ターゲットクラス固有のロジック。

これにはいくつかの欠点があります。

1)反射をよく理解する必要があります(これは少し複雑で、初心者向けではありません)。2)一部のJavaクラスと実際にサードパーティのライブラリは(驚いたことに)適切にコーディングされていません。つまり、単一の文字列引数を入力として受け取り、ターゲットクラスのインスタンスを返すメソッドがありますが、それはあなたが考えていることではありません... Integerクラスを考えてみましょう。

static Integer getInteger(String nm)
      Determines the integer value of the system property with the specified name.

上記のメソッドは、プリミティブintをラップするオブジェクトとしての整数とは実際には何の関係もありません。Reflectionは、これを、文字列から整数を誤って作成する可能性のある候補として、デコード、valueof、およびコンストラクターのメンバーと比較して見つけます。これらはすべて、入力データを実際に制御することはできないが、それが整数である可能性があるかどうかを知っています。

上記を修正するには、例外をスローするメソッドを探すことから始めるとよいでしょう。そのようなオブジェクトのインスタンスを作成する無効な入力値は例外スローするはずだからです。残念ながら、例外がチェック済みとして宣言されているかどうかに関しては、実装が異なります。たとえば、Integer.valueOf(String)はチェックされたNumberFormatExceptionをスローしますが、リフレクションルックアップ中にPattern.compile()例外は見つかりません。繰り返しになりますが、この動的な「クロスキャスティング」アプローチの失敗ではなく、オブジェクト作成メソッドの例外宣言の非常に非標準的な実装だと思います。

上記の実装方法の詳細が必要な場合はお知らせください。ただし、このソリューションは、型安全性の優れた部分を失うことなく、はるかに柔軟で拡張性があり、コードが少ないと思います。もちろん、「あなたのデータを知る」ことは常に最善ですが、私たちの多くが見つけるように、私たちは時々管理されていないコンテンツの受信者であり、それを適切に使用するために最善を尽くさなければなりません。

乾杯。


1

ですから、これは古い投稿ですが、何か貢献できると思います。

あなたはいつでもこのようなことをすることができます:

package com.dyna.test;  

import java.io.File;  
import java.lang.reflect.Constructor;  

public class DynamicClass{  

  @SuppressWarnings("unchecked")  
  public Object castDynamicClass(String className, String value){  
    Class<?> dynamicClass;  

    try  
    {  
      //We get the actual .class object associated with the specified name  
      dynamicClass = Class.forName(className);  



    /* We get the constructor that received only 
     a String as a parameter, since the value to be used is a String, but we could
easily change this to be "dynamic" as well, getting the Constructor signature from
the same datasource we get the values from */ 


      Constructor<?> cons =  
        (Constructor<?>) dynamicClass.getConstructor(new Class<?>[]{String.class});  

      /*We generate our object, without knowing until runtime 
 what type it will be, and we place it in an Object as 
 any Java object extends the Object class) */  
      Object object = (Object) cons.newInstance(new Object[]{value});  

      return object;  
    }  
    catch (Exception e)  
    {  
      e.printStackTrace();  
    }  
    return null;  
  }  

  public static void main(String[] args)  
  {   
    DynamicClass dynaClass = new DynamicClass();  

    /* 
 We specify the type of class that should be used to represent 
 the value "3.0", in this case a Double. Both these parameters 
 you can get from a file, or a network stream for example. */  
    System.out.println(dynaClass.castDynamicClass("java.lang.Double", "3.0"));  

    /* 
We specify a different value and type, and it will work as 
 expected, printing 3.0 in the above case and the test path in the one below, as the Double.toString() and 
 File.toString() would do. */  
    System.out.println(dynaClass.castDynamicClass("java.io.File", "C:\\testpath"));  
  }  

もちろん、Javaは静的に型付けされた言語であるため、これは他の言語(Pythonなど)のように実際には動的なキャストではありません。ただし、これにより、識別子に応じて、実際にさまざまな方法でデータをロードする必要があるフリンジケースを解決できます。また、Stringパラメーターを使用してコンストラクターを取得する部分は、同じデータソースからそのパラメーターを渡すことで、おそらくより柔軟にすることができます。つまり、ファイルから、使用するコンストラクター署名と使用する値のリストを取得します。たとえば、最初のパラメーターは文字列であり、最初のオブジェクトは文字列としてキャストします。次のオブジェクトは整数などですが、プログラムの実行中に、最初にFileオブジェクトを取得し、次にDoubleなどを取得します。

このようにして、これらのケースを説明し、オンザフライでいくらか「動的」なキャストを行うことができます。

これがグーグル検索で現れ続けるので、これが誰にでも役立つことを願っています。


0

私も最近これをしなければならないと感じましたが、コードをよりきれいに見せ、より良いOOPを使用する別の方法を見つけました。

それぞれが特定のメソッドを実装する多くの兄弟クラスがあります doSomething()。そのメソッドにアクセスするには、最初にそのクラスのインスタンスが必要ですが、すべての兄弟クラスのスーパークラスを作成し、スーパークラスからメソッドにアクセスできるようになりました。

以下に、「動的鋳造」に代わる2つの方法を示します。

// Method 1.
mFragment = getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
switch (mUnitNum) {
case 0:
    ((MyFragment0) mFragment).sortNames(sortOptionNum);
    break;
case 1:
    ((MyFragment1) mFragment).sortNames(sortOptionNum);
    break;
case 2:
    ((MyFragment2) mFragment).sortNames(sortOptionNum);
    break;
}

そして私の現在使用している方法、

// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);

動的にキャストする必要はありません。メソッドdoSomething()を持つスーパークラスと、メソッドdoSomething()を実装するサブクラスを持つ式は、OOPの主な目的の1つであるポリモーフィズムです。オブジェクトfoo = new Integer( "1"); foo.toString()は1を返します。これはObjectに割り当てられていますが、整数であるため、そのように動作します。メソッドtoStringを実行すると、Integer実装が実行されます。ポリモーフィズム。
ジョージザビエル

0

私が非常に有用であり、同様のニーズを経験している誰かのために可能であると私が見つけた何かを投稿すると思っただけです。

次のメソッドは、JavaFXアプリケーション用に作成したメソッドで、キャストする必要がなく、コントローラーが返されるたびにオブジェクトxインスタンスのオブジェクトbステートメントを作成する必要もありません。

public <U> Optional<U> getController(Class<U> castKlazz){
    try {
        return Optional.of(fxmlLoader.<U>getController());
    }catch (Exception e){
        e.printStackTrace();
    }
    return Optional.empty();
}

コントローラを取得するためのメソッド宣言は

public <T> T getController()

クラスオブジェクトを介してメソッドに渡されたタイプUを使用することで、メソッドgetコントローラーに転送して、返すオブジェクトのタイプを通知できます。間違ったクラスが指定された場合にオプションのオブジェクトが返され、例外が発生した場合は、チェックできる空のオプションが返されます。

これは、メソッドへの最後の呼び出しがどのように見えるかです(返されたオプションのオブジェクトの存在がコンシューマーを取る場合

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());

0

ダイナミックキャスティングにこれを試してください。それが動作します!!!

    String something = "1234";
    String theType = "java.lang.Integer";
    Class<?> theClass = Class.forName(theType);
    Constructor<?> cons = theClass.getConstructor(String.class);
    Object ob =  cons.newInstance(something);
    System.out.println(ob.equals(1234));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.