Java语言中,想通过反射实例化一个抽象类的子类,问题也可以简化点,可以建个类继承这个抽象类,然后实例化子类。
[code="java"]
public abstract class MyClass {
MyClass() {
}
}
[/code]
在代码中可以这么写:
[code="java"]
new MyClass() {}
[/code]
但是如果通过反射,怎么把String这个type传进去,或者通过反射获得constructor,怎么把泛型信息传进去:
下面这个是个错误的写法,只是说明要实现的内容:
[code="java"]
new MyClass() {
}
[/code]
[code="java"]
为何一定要传个Java参数的类型标记,任何一个有意的标记,通过map自动映射过去不一样啦。例如现在spring3 mvc都是通过url和方法自动映射的
[/code]
不管你怎么映射,最后都是成为一个class类型标记,不然是不可能知道转成什么的。
无论你走多少弯路,最后都是一条出路。
Map.put("ajson","com.bean.A")
然后再Class.forname("com.bean.A")
而且这个ajson还得付在json里面,不累么?
传json过来,到底要转成什么样是不知道的,是由编程的人说了算。
LZ现在想法是遍历所有类型,看那个反射能匹配上就用哪个?这样不但要写的代码都得写,而且还要走一圈反射。
有时候不要想着太自动化,如果能够很自动化,j2ee里面就不会动不动就那么多配置文件了。
也许你会说不是可以自定义注解么?
但是对于这么简单的需求来说,再写个注解
[code="java"]
@FromJson("ajson")
class A(){}
[/code]
无非就是用一大段解析注解代码来换取
Map.put("ajson","com.bean.A")
Class.forname("com.bean.A")
这两行代码罢了。
可读性还差,更是没必要。
通过反射是做不到的,抽象类也不能实例化
@Test
public void reflect() throws Exception {
Class clazz = (Class) Class.forName("java.lang.String");
Constructor constructor = clazz.getConstructor(String.class);
String str = constructor.newInstance("ZhongGang");
System.out.println(str);
}
楼主你的意思没有表达清楚,不知是不是我理解错误
做不到的。
java的泛型跟C++不一样。
java的泛型仅限于编译的时候类型检查。List跟List经过编译后都是List,jvm是看不到泛型的。
所以反射这种基于字节码的动态特性是没办法跟泛型扯上关系的。
虽然对于java来说泛型再编译的时候被擦除了,但是始终还是能拿到它的信息的
[code="java"]
public class A extends B
protected Class entityClass;
public A() {
entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
[/code]
[quote]虽然对于java来说泛型再编译的时候被擦除了,但是始终还是能拿到它的信息的 [/quote]
都擦除了还怎么拿,你看清楚你拿到的是什么信息,E才是想要的,而不是强制转换的内部变量。都知道是Class类型了,当然拿得到。
去看看C++的模板类,就知道什么是真正的泛型了。
反射实例化一个抽象类,应该是不可的,因为反射只是一种语法约束而已,可利用内部类实例化匿名类。例如 new MyClass { //实现抽象方法}
好吧,楼主你就自己看吧,我也不说什么了,拿得到拿不到楼主看了就明白了
[url]http://www.iteye.com/topic/585900[/url]
[code="java"]好吧,楼主你就自己看吧,我也不说什么了,拿得到拿不到楼主看了就明白了 [/code]
你先看清楚吧,不要误导了LZ。
MyClass类中的“T”你能拿到?
说了 类型擦除,T永远是被擦除掉的,反射只能操作字节码,字节码中有的就能获取,T属于泛型,字节码中没有。
new MyClass这个是泛化的么?文章中的方式就是获取这种非泛化类型,毫无意义,所以说不要把反射跟泛型扯在一起,只会让人误导java的泛型有多强大似的。
说了,只能用来做编译检查。
还以为要实例化一个抽象类的,不过,楼主为何要这样做呢?转换成一个泛型类,然后利用泛型简繁重复代码,泛型适用的情况,只是为了更好的编译,减少类型转换的错误。为何不考虑一下设计模式,转换成命令模式,回调方法不是一样可以实现效果
[quote]3.存在的问题
if(m.getName().equals("testReflect"))
这种方式的判断肯定是不可靠的,有重载的方法就不好使了。参数个数不一样的重载还好办,可以通过判断参数个数来解决。针对目前我们的应用来说勉强可以,但是不够通用。如果参数个数一样类型不一样就不好办了。这样还得判断json内容和参数属性的匹配,不知道有没有更好的方法。 [/quote]
LZ其实你绕了一大圈又回到了起点,你现在的“参数个数一样类型不一样就不好办了”这个问题不就是刚开始你提的么。
[code="java"]public void testMethod(List list);
public void testMethod2(ClassB b); [/code]
你想用一个[code="java"]public void testMethod(T t);[/code]搞定
问题在于java在运行时是不会知道T是什么的,T反射时是没办法动态化的,你只有通过写代码告诉编译器T是什么类型才行。
[code="java"]public void testReflect(List psrList)[/code]
就是你告诉编译器T是list类型,所以换成别的类型比如String,你就必须再写一个[code="java"]public void testReflect(String string)[/code]
这就是为什么像命令模式了,你告诉他什么类型,然后就执行什么类型的方法。
按照我目前的理解,可能有错,仅供参考,你只要把json转换出来的bean包装成一个命令,无论是list还是普通的bean都转换成一个通用的命令,然后写一个通用的执行方法,执行命令,获取执行结果,无论以后从json获取的东西怎么变,执行命令的方式获取执行结果都不会受影响,你也可参考 [url]http://www.iteye.com/topic/1121817[/url]
[quote]通过方法名和参数个数做第一层过滤,然后直接使用符合条件的第一个方法的参数转换json,如果没有抛异常,认为调用方法正确,如果抛异常了,就尝试用下一个方法的参数转换。
这样基本可以解决我提出的问题,但是对于参数个数相同,类型不同的重载方法,存在效率问题。但是如果这种类型的重载方法很少,也可以勉强接受。还好,我们的系统只有一个方法存在这样的问题,而且调用频率极低,有时间做个测试,看看效率差多少。[/quote]
你还不如对你传进去的json做过滤呢,看这种json格式是需要转成list的还是string的还是map的。无非就是校验格式。看看嵌套几层。校验完成后就可以回调相应的方法。
反射遍历,通过反射获取的method又没有缓存,效率会很低,而且还把运行时异常作为逻辑来使用,不太合适。
写个枚举类,写个校验方法,输入的json字符串,然后输出对应的格式枚举类型,然后直接调用该枚举类型的转换方法。
以后要是新增转换类型,无非就是新加个枚举,这跟你新写个重载方法代码量差不多。
按照楼主的思路去考虑,感觉最好要有一个标记来映射json数据和调用具体的方法,这样就不用去一个个方法去尝试了。现在很多流行的对外开放平台,都是能直接映射方法的。
这个类型本身就是你们系统定义的,当然得自己写代码的时候传。
就一个普通的字符串:{'result':'201','bean':{'result':'201'}}
你不写类型程序肯定不知道转成哪个bean啊。至于这个类型来自文件,还是代码里写死,这个是可以做到的。现在的问题点是你反射一大圈最后还是自己写类型。因为java里面没有类似于C++的模板。
你要么传java类型,要么预留接口传脚本(如嵌入groovy,ruby)。
也就是说你不管怎么绕,类型永远要传。类型不一致造成的冗余代码永远要写。
下面的代码同样做了你反射做的事情:
[code="java"]
public final class JsonUtils
{
private static ObjectMapper mapper = new ObjectMapper();
private JsonUtils()
{
}
public static <T> Object toBean(String json, Class<T> type, Parser<T> parser)
throws Exception
{
T obj = mapper.readValue(json, type);
Object result = parser.testReflect(obj);
return result;
}
static interface Parser<T>
{
Object testReflect(T obj);
}
}
[/code]
测试:
[code="java"]
public class TestJsonUtil
{
public static void main(String[] args) throws Exception
{
Object obj = JsonUtils.toBean(
"{'result':'201','bean':{'result':'201'}}", Bean.class,
new JsonUtils.Parser<Bean>()
{
@Override
public Object testReflect(Bean obj)
{
return obj;
}
});
System.out.println(((Bean) obj).getBean().getResult());
}
public static class Bean
{
String result;
Bean2 bean;
public Bean2 getBean()
{
return bean;
}
public void setBean(Bean2 bean)
{
this.bean = bean;
}
public String getResult()
{
return result;
}
public void setResult(String result)
{
this.result = result;
}
}
public static class Bean2
{
String result;
public String getResult()
{
return result;
}
public void setResult(String result)
{
this.result = result;
}
}
}
[/code]
晕,测试后发现JsonUtils还要加配置项,
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
LZ莫怪。应该加一行,如下:
[code="java"]
public final class JsonUtils
{
private static ObjectMapper mapper = new ObjectMapper();
static
{
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
}
private JsonUtils()
{
}
public static <T> Object toBean(String json, Class<T> type, Parser<T> parser)
throws Exception
{
T obj = mapper.readValue(json, type);
Object result = parser.testReflect(obj);
return result;
}
static interface Parser<T>
{
Object testReflect(T obj);
}
}
[/code]
[quote]aronlulu 的方式是可取的,不过,lz的意思现在是如何关联json和具体的实现类[/quote],[quote]其实客户端是知道请求参数类型的,完全可以传过来,但是我们的系统是开放给第三方的,在webservice中包一层json,第三方可能是Java也可能是C#的或者别的语言,所以让C#的客户端传个Java参数的类型标记,总觉得说不通。[/quote] 为何一定要传个Java参数的类型标记,任何一个有意的标记,通过map自动映射过去不一样啦。例如现在spring3 mvc都是通过url和方法自动映射的
讨论到这种程度,已经很不错了,lz也已经有了可选的方案了。希望以后多多交流
[code="java"]readValue2List(String jsonStr, Class tClass ) {
try {
TypeReference> typeRef = new TypeReference>() {
@Override
public Type getType(){
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[]{tClass};
}
@Override
public Type getRawType() {
return List.class;
}
@Override
public Type getOwnerType() {
return null;
}
};
}
};
return mapper.readValue(jsonStr, typeRef);
} catch (Exception e) {
throw new RuntimeException("not able to convert json string:"
+ jsonStr);
}
}[/code]