java:如何将变量从一种类型动态转换为另一种类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2127318/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
java: How can I do dynamic casting of a variable from one type to another?
提问by ufk
I would like to do dynamic casting for a Java variable, the casting type is stored in a different variable.
我想对 Java 变量进行动态转换,转换类型存储在不同的变量中。
This is the regular casting:
这是常规铸造:
String a = (String) 5;
This is what I want:
这就是我要的:
String theType = 'String';
String a = (theType) 5;
Is this possible, and if so how? Thanks!
这可能吗,如果可以,怎么办?谢谢!
Update
更新
I'm trying to populate a class with a HashMap
that I received.
我正在尝试用HashMap
我收到的a 填充一个类。
This is the constructor:
这是构造函数:
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());
}
}
}
The problem here is that some of the class' variables are of type Double
, and if the number 3 is received it sees it as Integer
and I have type problem.
这里的问题是一些类的变量是类型的Double
,如果收到数字 3,它会将其视为Integer
类型问题。
采纳答案by akuhn
Regarding your update, the only way to solve this in Java is to write code that covers all cases with lots of
if
andelse
andinstanceof
expressions. What you attempt to do looks as if are used to program with dynamic languages. In static languages, what you attempt to do is almost impossible and one would probably choose a totally different approach for what you attempt to do. Static languages are just not as flexible as dynamic ones :)Good examples of Java best practice are the answer by BalusC(ie
ObjectConverter
) and the answer by Andreas_D(ieAdapter
) below.
关于你的更新,在 Java 中解决这个问题的唯一方法是编写代码,用大量的
if
andelse
和instanceof
表达式覆盖所有情况。您尝试做的事情看起来好像用于使用动态语言进行编程。在静态语言中,您尝试做的事情几乎是不可能的,人们可能会为您尝试做的事情选择一种完全不同的方法。静态语言不如动态语言灵活:)Java 最佳实践的好例子是下面BalusC(ie
ObjectConverter
) 的答案和 Andreas_D(ieAdapter
)的答案。
That does not make sense, in
这没有意义,在
String a = (theType) 5;
the type of a
is statically bound to be String
so it does not make any sense to have a dynamic cast to this static type.
的类型a
是静态绑定的,String
因此对这种静态类型进行动态转换没有任何意义。
PS:The first line of your example could be written as Class<String> stringClass = String.class;
but still, you cannot use stringClass
to cast variables.
PS:您的示例的第一行可以写成,Class<String> stringClass = String.class;
但仍然不能用于stringClass
转换变量。
回答by BalusC
You'll need to write sort of ObjectConverter
for this. This is doable if you have both the object which you want to convert and you know the target class to which you'd like to convert to. In this particular case you can get the target class by Field#getDeclaringClass()
.
你需要为此写一些ObjectConverter
。如果您拥有要转换的对象并且知道要转换为的目标类,则这是可行的。在这种特殊情况下,您可以通过Field#getDeclaringClass()
.
You can find herean example of such an ObjectConverter
. It should give you the base idea. If you want more conversion possibilities, just add more methods to it with the desired argument and return type.
您可以在此处找到此类ObjectConverter
. 它应该给你基本的想法。如果您想要更多的转换可能性,只需添加更多具有所需参数和返回类型的方法即可。
回答by Andreas Dolk
It works and there's even a common pattern for your approach: the Adapter pattern. But of course, (1) it does not work for casting java primitives to objects and (2) the class has to be adaptable (usually by implementing a custom interface).
它有效,您的方法甚至有一个通用模式:Adapter 模式。但是当然,(1) 它不适用于将 java 原语转换为对象,并且 (2) 该类必须具有适应性(通常通过实现自定义接口)。
With this pattern you could do something like:
使用此模式,您可以执行以下操作:
Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);
and the getAdapter method in Wolf 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
}
For you special idea - that is impossible. You can't use a String value for casting.
对你来说特别的想法——那是不可能的。您不能使用 String 值进行转换。
回答by Michael Borgwardt
Your problem is not the lack of "dynamic casting". Casting Integer
to Double
isn't possible at all. You seem to want to give Java an object of one type, a field of a possibly incompatible type, and have it somehow automatically figure out how to convert between the types.
你的问题不在于缺乏“动态铸造”。根本不可能投射Integer
到Double
。您似乎想要给 Java 一个类型的对象,一个可能不兼容类型的字段,并让它以某种方式自动找出如何在类型之间进行转换。
This kind of thing is anathema to a strongly typed language like Java, and IMO for very good reasons.
出于很好的理由,这种事情对于像 Java 和 IMO 这样的强类型语言来说是令人厌恶的。
What are you actually trying to do? All that use of reflection looks pretty fishy.
你究竟想做什么?所有反射的使用看起来都很可疑。
回答by user85421
Yes it is possible using Reflection
是的,可以使用反射
Object something = "something";
String theType = "java.lang.String";
Class<?> theClass = Class.forName(theType);
Object obj = theClass.cast(something);
but that doesn't make much sense since the resulting object must be saved in a variable of Object
type. If you need the variable be of a given class, you can just cast to that class.
但这没有多大意义,因为结果对象必须保存在Object
类型变量中。如果您需要变量属于给定类,则可以将其强制转换为该类。
If you want to obtain a given class, Number
for example:
如果要获取给定的类,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);
but there is still no point doing it so, you could just cast to Number
.
但是这样做仍然没有意义,您可以将其强制转换为Number
.
Casting of an object does NOT change anything; it is just the waythe compiler treats it.
The only reason to do something like that is to check if the object is an instance of the given class or of any subclass of it, but that would be better done using instanceof
or Class.isInstance()
.
对象的投射不会改变任何东西;这只是编译器处理它的方式。
这样做的唯一原因是检查对象是否是给定类或其任何子类的实例,但最好使用instanceof
or来完成Class.isInstance()
。
Update
更新
according your last updatethe real problem is that you have an Integer
in your HashMap
that should be assigned to a Double
. What you can do in this case, is check the type of the field and use the xxxValue()
methods of Number
根据您上次更新的真正的问题是,你有一个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);
...
(not sure if I like the idea of having the wrong type in the Map
)
(不确定我是否喜欢在 中输入错误类型的想法Map
)
回答by Bandi-T
Don't do this. Just have a properly parameterized constructor instead. The set and types of the connection parameters are fixed anyway, so there is no point in doing this all dynamically.
不要这样做。只需有一个正确参数化的构造函数。连接参数的设置和类型无论如何都是固定的,因此动态执行这些操作毫无意义。
回答by Jared Russell
You can do this using the Class.cast()
method, which dynamically casts the supplied parameter to the type of the class instance you have. To get the class instance of a particular field, you use the getType()
method on the field in question. I've given an example below, but note that it omits all error handling and shouldn't be used unmodified.
您可以使用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);
}
}
回答by Darrell Teague
For what it is worth, most scripting languages (like Perl) and non-static compile-time languages (like Pick) support automatic run-time dynamic String to (relatively arbitrary) object conversions. This CAN be accomplished in Java as well without losing type-safety and the good stuff statically-typed languages provide WITHOUT the nasty side-effects of some of the other languages that do evil things with dynamic casting. A Perl example that does some questionable math:
就其价值而言,大多数脚本语言(如 Perl)和非静态编译时语言(如 Pick)支持自动运行时动态字符串到(相对任意的)对象转换。这也可以在 Java 中完成,而不会失去类型安全性,并且静态类型语言提供的好东西没有其他一些用动态转换做坏事的语言的讨厌的副作用。一个 Perl 示例,它进行了一些有问题的数学运算:
print ++($foo = '99'); # prints '100'
print ++($foo = 'a0'); # prints 'a1'
In Java, this is better accomplished (IMHO) by using a method I call "cross-casting". With cross-casting, reflection is used in a lazy-loaded cache of constructors and methods that are dynamically discovered via the following static method:
在 Java 中,使用我称为“交叉转换”的方法可以更好地完成(恕我直言)。通过交叉转换,反射用于构造函数和方法的延迟加载缓存,这些构造函数和方法是通过以下静态方法动态发现的:
Object fromString (String value, Class targetClass)
Unfortunately, no built-in Java methods such as Class.cast() will do this for String to BigDecimal or String to Integer or any other conversion where there is no supporting class hierarchy. For my part, the point is to provide a fully dynamic way to achieve this - for which I don't think the prior reference is the right approach - having to code every conversion. Simply put, the implementation is just to cast-from-string if it is legal/possible.
不幸的是,对于 String 到 BigDecimal 或 String 到 Integer 或任何其他没有支持类层次结构的转换,没有内置的 Java 方法(例如 Class.cast())可以执行此操作。就我而言,重点是提供一种完全动态的方式来实现这一点 - 我认为先前的参考不是正确的方法 - 必须对每次转换进行编码。简而言之,如果合法/可能,实现只是从字符串转换。
So the solution is simple reflection looking for public Members of either:
所以解决方案是简单的反射寻找以下任一公共成员:
STRING_CLASS_ARRAY = (new Class[] {String.class});
STRING_CLASS_ARRAY = (new Class[] {String.class});
a) Member member = targetClass.getMethod(method.getName(),STRING_CLASS_ARRAY); b) Member member = targetClass.getConstructor(STRING_CLASS_ARRAY);
a) Member member = targetClass.getMethod(method.getName(),STRING_CLASS_ARRAY); b) Member member = targetClass.getConstructor(STRING_CLASS_ARRAY);
You will find that all of the primitives (Integer, Long, etc) and all of the basics (BigInteger, BigDecimal, etc) and even java.regex.Pattern are all covered via this approach. I have used this with significant success on production projects where there are a huge amount of arbitrary String value inputs where some more strict checking was needed. In this approach, if there is no method or when the method is invoked an exception is thrown (because it is an illegal value such as a non-numeric input to a BigDecimal or illegal RegEx for a Pattern), that provides the checking specific to the target class inherent logic.
您会发现所有基元(Integer、Long 等)和所有基础(BigInteger、BigDecimal 等)甚至 java.regex.Pattern 都通过这种方法涵盖。我在生产项目中使用它取得了巨大的成功,在这些项目中有大量的任意字符串值输入,需要更严格的检查。在这种方法中,如果没有方法或调用方法时抛出异常(因为它是非法值,例如 BigDecimal 的非数字输入或模式的非法 RegEx),提供特定于目标类的内在逻辑。
There are some downsides to this:
这有一些缺点:
1) You need to understand reflection well (this is a little complicated and not for novices). 2) Some of the Java classes and indeed 3rd-party libraries are (surprise) not coded properly. That is, there are methods that take a single string argument as input and return an instance of the target class but it isn't what you think... Consider the Integer class:
1)你需要很好地理解反射(这个有点复杂,不适合新手)。2) 一些 Java 类和实际上 3rd-party 库(令人惊讶)没有正确编码。也就是说,有些方法将单个字符串参数作为输入并返回目标类的一个实例,但这不是您所想的……考虑 Integer 类:
static Integer getInteger(String nm)
Determines the integer value of the system property with the specified name.
The above method really has nothing to do with Integers as objects wrapping primitives ints. Reflection will find this as a possible candidate for creating an Integer from a String incorrectly versus the decode, valueof and constructor Members - which are all suitable for most arbitrary String conversions where you really don't have control over your input data but just want to know if it is possible an Integer.
上面的方法实际上与整数作为包装原始整数的对象无关。反射会发现这是从字符串错误地创建整数的可能候选者,而不是解码、值和构造函数成员——它们都适用于大多数任意字符串转换,其中您确实无法控制输入数据但只想知道是否可能是整数。
To remedy the above, looking for methods that throw Exceptions is a good start because invalid input values that create instances of such objects shouldthrow an Exception. Unfortunately, implementations vary as to whether the Exceptions are declared as checked or not. Integer.valueOf(String) throws a checked NumberFormatException for example, but Pattern.compile() exceptions are not found during reflection lookups. Again, not a failing of this dynamic "cross-casting" approach I think so much as a very non-standard implementation for exception declarations in object creation methods.
为了解决上述问题,寻找抛出异常的方法是一个好的开始,因为创建此类对象实例的无效输入值应该抛出异常。不幸的是,对于异常是否声明为已检查,实现各不相同。例如,Integer.valueOf(String) 抛出一个检查过的 NumberFormatException,但在反射查找期间没有发现 Pattern.compile() 异常。同样,我认为这不是这种动态“交叉转换”方法的失败,而是对象创建方法中异常声明的非常非标准的实现。
If anyone would like more details on how the above was implemented, let me know but I think this solution is much more flexible/extensible and with less code without losing the good parts of type-safety. Of course it is always best to "know thy data" but as many of us find, we are sometimes only recipients of unmanaged content and have to do the best we can to use it properly.
如果有人想了解有关如何实现上述内容的更多详细信息,请告诉我,但我认为此解决方案更加灵活/可扩展,并且代码更少,而不会丢失类型安全的优点。当然,“了解您的数据”总是最好的,但正如我们许多人所发现的,我们有时只是非托管内容的接收者,必须尽我们所能正确使用它。
Cheers.
干杯。
回答by Acapulco
So, this is an old post, however I think I can contribute something to it.
所以,这是一个旧帖子,但我认为我可以为它做出一些贡献。
You can always do something like this:
你总是可以做这样的事情:
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"));
}
Of course, this is not really dynamic casting, as in other languages (Python for example), because java is a statically typed lang. However, this can solve some fringe cases where you actually need to load some data in different ways, depending on some identifier. Also, the part where you get a constructor with a String parameter could be probably made more flexible, by having that parameter passed from the same data source. I.e. from a file, you get the constructor signature you want to use, and the list of values to be used, that way you pair up, say, the first parameter is a String, with the first object, casting it as a String, next object is an Integer, etc, but somehwere along the execution of your program, you get now a File object first, then a Double, etc.
当然,这并不是真正的动态转换,就像在其他语言(例如 Python)中那样,因为 java 是静态类型的 lang。但是,这可以解决一些边缘情况,您实际上需要根据某些标识符以不同方式加载某些数据。此外,通过从同一数据源传递该参数,您获得带有 String 参数的构造函数的部分可能会更加灵活。即从文件中,您获得要使用的构造函数签名,以及要使用的值列表,这样您就可以配对,例如,第一个参数是一个字符串,第一个对象将其转换为字符串, next 对象是 Integer 等,但是在执行程序的过程中,您现在首先获得 File 对象,然后是 Double 等。
In this way, you can account for those cases, and make a somewhat "dynamic" casting on-the-fly.
通过这种方式,您可以考虑这些情况,并即时进行某种程度的“动态”投射。
Hope this helps anyone as this keeps turning up in Google searches.
希望这对任何人都有帮助,因为这在 Google 搜索中不断出现。
回答by Anonsage
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.
我最近觉得我也必须这样做,但后来找到了另一种可能使我的代码看起来更整洁并使用更好的 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.
我有许多兄弟类,每个类都实现了某种方法doSomething()
。为了访问该方法,我必须首先拥有该类的实例,但是我为所有兄弟类创建了一个超类,现在我可以从超类访问该方法。
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);