95

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型の問題があることです。

4

13 に答える 13

129

はい、リフレクションを使用して可能です

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

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

アップデート

最後の更新によると、本当の問題は、IntegerHashMap割り当てる必要がある があること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

于 2010-01-24T14:35:21.157 に答える
23

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

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

于 2010-01-24T14:29:05.477 に答える
14

あなたの更新に関して、Java でこれを解決する唯一の方法は、多くの and 式を含むすべてのケースをカバーするコードを記述するifことelseですinstanceof。あなたがしようとしていることは、動的言語でのプログラミングに使用されているかのように見えます。静的言語では、あなたがやろうとしていることはほとんど不可能であり、おそらくあなたがやろうとしていることに対してまったく異なるアプローチを選択するでしょう. 静的言語は動的言語ほど柔軟ではありません:)

Java のベスト プラクティスの良い例は、以下の BalusCによる回答(つまりObjectConverter) とAndreas_D による回答(つまりAdapter) です。


それは意味がありません。

String a = (theType) 5;

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

PS: 例の最初の行は次のように記述できますが、変数をキャストするためにClass<String> stringClass = String.class;使用することはできません。stringClass

于 2010-01-24T14:17:23.130 に答える
13

メソッドを使用してこれを行うことができます。この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);
    }
}
于 2010-01-24T14:57:01.700 に答える
8

以下のような簡単な 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());          
    }    
}

}
于 2016-05-04T07:29:23.117 に答える
5

それは機能し、あなたのアプローチには共通のパターンもあります: Adapter パターン. しかしもちろん、(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
}

あなたの特別な考えにとって、それは不可能です。キャストに String 値を使用することはできません。

于 2010-01-24T14:29:32.903 に答える
2

あなたの問題は、「動的キャスト」の欠如ではありません。IntegerへのキャストDoubleはまったくできません。Java に 1 つのタイプのオブジェクト、おそらく互換性のないタイプのフィールドを指定し、何らかの形でタイプ間の変換方法を自動的に把握させたいと考えているようです。

この種のことは、Java のような強く型付けされた言語や、非常に正当な理由から IMO にとって忌み嫌われます。

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

于 2010-01-24T14:29:48.097 に答える
1

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

于 2010-01-24T14:43:57.243 に答える
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 パラメーターを使用してコンストラクターを取得する部分は、そのパラメーターを同じデータ ソースから渡すことで、おそらくより柔軟になる可能性があります。つまり、ファイルから、使用するコンストラクター シグネチャと使用する値のリストを取得します。たとえば、最初のパラメーターが文字列で、最初のオブジェクトが文字列としてキャストされているとします。次のオブジェクトは Integer などですが、プログラムの実行中に何らかの形で、最初に File オブジェクトを取得し、次に Double などを取得します。

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

これはGoogle検索で引き続き表示されるため、これが誰にとっても役立つことを願っています.

于 2012-11-07T15:02:54.060 に答える
1

価値があるのは、ほとんどのスクリプト言語 (Perl など) と非静的コンパイル時言語 (Pick など) が、ランタイム動的 String から (比較的任意の) オブジェクトへの自動変換をサポートしていることです。これは、型安全性を失うことなく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 への非数値入力やパターンの不正な RegEx などの不正な値であるため)。ターゲット クラス固有のロジック。

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

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

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

上記の方法は、プリミティブ int をラップするオブジェクトとしての整数とはまったく関係ありません。リフレクションはこれを、decode、valueof、constructor メンバーと比較して、String から誤って Integer を作成する可能性のある候補として見つけます。これらはすべて、実際には入力データを制御できないが、単に変換したい場合のほとんどの任意の String 変換に適しています。整数が可能かどうかを知っています。

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

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

乾杯。

于 2011-06-27T16:59:24.273 に答える
0

私が非常に有用であり、同様のニーズを経験している人にとって可能であると思われるものを投稿しようと思いました.

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

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 コントローラーに転送して、返すオブジェクトの型を伝えることができます。間違ったクラスが指定された場合にオプションのオブジェクトが返され、例外が発生した場合、チェックできる空のオプションが返されます。

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

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());
于 2015-05-07T01:04:23.713 に答える
0

I recently felt like I had to do this too, but then found another way which possibly makes my code look neater, and uses better OOP.

I have many sibling classes that each implement a certain method doSomething(). In order to access that method, I would have to have an instance of that class first, but I created a superclass for all my sibling classes and now I can access the method from the superclass.

Below I show two ways alternative ways to "dynamic casting".

// 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;
}

and my currently used method,

// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);
于 2013-08-16T21:09:01.507 に答える