java:我怎么能做一个variables从一个types的dynamic铸造到另一个?
我想做一个Javavariables的dynamic铸造,铸造types存储在一个不同的variables。
这是正规的铸造:
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()); } } }
这里的问题是,一些类variables是双重types,如果接收到数字3它看到它是整数,我有types的问题。
关于你的更新,在Java中解决这个问题的唯一方法是编写覆盖所有大量
if
和else
和instanceof
expression式的代码。 你试图做什么,看起来好像用于dynamic语言编程。 在静态语言中,你试图做的事情几乎是不可能的,你可能会select一个完全不同的方法来做你想做的事情。 静态语言只是不如dynamic的:)Java最佳实践的好例子是BalusC (即
ObjectConverter
)的答案和Andreas_D (即Adapter
)的答案 。
这没有意义,在
String a = (theType) 5;
a的types被静态绑定为String
因此对此静态types进行dynamic转换没有任何意义。
PS: 你的例子的第一行可以写成Class<String> stringClass = String.class;
但仍然不能使用stringClass
来转换variables。
是的,这是可以使用reflection
Object something = "something"; String theType = "java.lang.String"; Class<?> theClass = Class.forName(theType); Object obj = theClass.cast(something);
但是这并没有什么意义,因为结果对象必须保存在一个Objecttypes的variables中。 如果你需要一个给定的类的variables,你可以投到那个类。
如果你想获得一个给定的类,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
,但使用instanceof
或Class.isInstance()
会更好。
更新
根据你最近的更新 ,真正的问题是你的HashMap中有一个整数应该被分配给Double。 在这种情况下你可以做的是检查字段的types并使用Number的xxxValue()
方法
... 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); ...
(不知道我是否喜欢在地图中input错误types的想法)
你需要为此编写一个ObjectConverter
。 这是可行的,如果你有你想要转换的对象,你知道你想转换到的目标类。 在这种情况下,您可以通过Field#getDeclaringClass()
获取目标类。
你可以在这里find这样一个ObjectConverter
的例子。 它应该给你基本的想法。 如果你想要更多的转换可能性,只需要添加更多的方法与所需的参数和返回types。
您可以使用Class.cast()
方法执行此操作,该方法将提供的参数dynamic转换为您所拥有的类实例的types。 要获取特定字段的类实例,可以在有问题的字段上使用getType()
方法。 我已经在下面给出了一个例子,但是请注意,它省略了所有的error handling,不应该不加修改地使用。
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)它不适用于将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值进行强制转换。
你的问题不是缺乏“dynamic铸造”。 将Integer
为Double
是不可能的。 您似乎想给Java一个types的对象,一个可能不兼容的types的字段,并以某种方式自动找出如何在types之间进行转换。
这种事情对于像Java这样的强types语言和IMO来说是非常好的原因。
你究竟在做什么? 所有reflection的使用看起来很可疑。
你可以写一个简单的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()); } } }
不要这样做。 只要有一个正确的参数化构造函数。 无论如何,连接参数的集合和types是固定的,所以没有必要dynamic地这样做。
大多数脚本语言(比如Perl)和非静态编译时语言(比如Pick)支持自动运行时dynamicstring到(相对任意的)对象转换。 这可以在Java中完成,而不会丢失types安全性,而好的东西静态types语言提供了一些其他语言的恶意副作用,这些副作用是dynamic强制转换的恶作剧。 一个Perl的例子,做一些可疑的math:
print ++($foo = '99'); # prints '100' print ++($foo = 'a0'); # prints 'a1'
在Java中,通过使用我称之为“交叉铸造”的方法可以更好地完成(恕我直言)。 通过交叉转换,reflection用于通过以下静态方法dynamic发现的构造函数和方法的caching加载caching中:
Object fromString (String value, Class targetClass)
不幸的是,没有像Class.cast()这样的内置Java方法会将String转换为BigDecimal,或者将String转换为Integer,或者在没有支持类层次结构的情况下进行其他转换。 就我而言,关键是要提供一个完全dynamic的方式来实现这一点 – 我不认为事先参考是正确的方法 – 必须编码每个转换。 简而言之,如果合法/可能,实施只是从string中进行投射。
所以解决方法是简单的反思寻找公众成员之一:
STRING_CLASS_ARRAY =(new Class [] {String.class});
a)成员member = targetClass.getMethod(method.getName(),STRING_CLASS_ARRAY); b)成员member = targetClass.getConstructor(STRING_CLASS_ARRAY);
你会发现所有的基本types(Integer,Long等)和所有的基础(BigInteger,BigDecimal等)甚至java.regex.Pattern都是通过这种方法来覆盖的。 我曾经在生产项目中取得了巨大的成功,在那里有大量的任意string值input,需要更严格的检查。 在这种方法中,如果没有方法或者调用方法的时候抛出一个exception(因为它是一个非法的值,例如一个BigDecimal的非数字input或者一个Pattern的非法RegEx),它提供了特定于目标类的内在逻辑。
这有一些缺点:
1)你需要很好地理解反思(这有点复杂,不适合新手)。 2)一些Java类和实际上的第三方库(惊喜)没有正确编码。 也就是说,有一些方法将单个string参数作为input,并返回目标类的实例,但这不是您想象的那样…考虑Integer类:
static Integer getInteger(String nm) Determines the integer value of the system property with the specified name.
上面的方法实际上与整数没有任何关系作为对象包装原始数据。 reflection会发现这是一个可能的候选人,从string错误地创build一个整数与解码,valueof和构造函数成员 – 这都是适合大多数任意的string转换,你真的没有控制你的input数据,但只是想知道是否可能是一个整数。
要解决上述问题,寻找抛出exception的方法是一个好的开始,因为创build这种对象的实例的无效input值应抛出exception。 不幸的是,实现方式会因exception是否被声明为checked而有所不同。 Integer.valueOf(String)将引发一个选中的NumberFormatException,但在reflection查找期间找不到Pattern.compile()exception。 再一次,不是这种dynamic的“交叉转换”方法的失败,我认为是在对象创build方法中exception声明的非标准实现。
如果有人想了解更多的细节,请告诉我,但是我认为这个解决scheme更加灵活/可扩展,代码less,不会丢失types安全性的好处。 当然,“知道你的数据”总是最好的,但是我们很多人发现,我们有时只是非托pipe内容的接收者,必须尽我们所能来正确使用它。
干杯。
所以,这是一个老post,但是我认为我可以为此做出贡献。
你总是可以这样做:
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")); }
当然,这不是真正的dynamic转换,就像其他语言(例如Python)一样,因为java是一个静态types的lang。 然而,这可以解决一些附带的情况,你实际上需要以不同的方式加载一些数据,这取决于一些标识符。 另外,通过从相同的数据源传递参数,可以使得使用String参数构造函数的部分更加灵活。 即从一个文件,你得到你想要使用的构造函数签名,以及要使用的值的列表,这样你配对,比如说,第一个参数是一个string,第一个对象,将其转换为一个string,下一个对象是Integer等,但是在执行程序的时候,你先得到一个File对象,然后是一个Double等等。
通过这种方式,您可以对这些情况进行说明,并在飞行中进行一些“dynamic”投射。
希望这可以帮助任何人,因为这不断在谷歌search。
我最近觉得我也必须这样做,但后来发现另一种可能使我的代码看起来更整洁,并使用更好的面向对象的方式。
我有许多兄弟类,每个实现一个特定的方法doSomething()
。 为了访问这个方法,我必须首先获得这个类的一个实例,但是我为所有的兄弟类创build了一个超类,现在我可以从超类访问这个方法。
下面我将展示两种替代“dynamic投射”的方法。
// 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);
只是觉得我会发布一些我觉得很有用的东西,对于有类似需求的人也是可以的。
下面的方法是我为JavaFX应用程序编写的一种方法,以避免每次返回控制器时都必须进行强制转换,并避免编写对象b语句的对象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()
通过使用通过类对象传入我的方法的typesU,它可以被转发到方法get控制器来告诉它返回什么types的对象。 如果提供了错误的类,则返回一个可选对象,并且发生exception,在这种情况下,将返回一个空的可选项,我们可以检查它。
这是对方法的最终调用看起来像(如果出现的可选对象返回消费者
getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());
试试这个dynamic铸造。 它会工作!
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));