iteye_19391
2012-03-23 10:05
浏览 871
已采纳

Java通过反射实例化泛型类的问题

Java语言中,想通过反射实例化一个抽象类的子类,问题也可以简化点,可以建个类继承这个抽象类,然后实例化子类。

[code="java"]
public abstract class MyClass {
MyClass() {
}

}
[/code]

在代码中可以这么写:
[code="java"]
new MyClass() {}
[/code]

但是如果通过反射,怎么把String这个type传进去,或者通过反射获得constructor,怎么把泛型信息传进去:

下面这个是个错误的写法,只是说明要实现的内容:
[code="java"]
new MyClass() {
}
[/code]

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

19条回答 默认 最新

  • Aronlulu 2012-03-26 20:02
    已采纳

    [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")
    这两行代码罢了。
    可读性还差,更是没必要。

    已采纳该答案
    打赏 评论
  • _1_1_7_ 2012-03-23 11:05

    通过反射是做不到的,抽象类也不能实例化

    打赏 评论
  • DreamZhong 2012-03-23 11:13

    @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);
    }

    楼主你的意思没有表达清楚,不知是不是我理解错误

    打赏 评论
  • Aronlulu 2012-03-23 15:24

    做不到的。
    java的泛型跟C++不一样。
    java的泛型仅限于编译的时候类型检查。List跟List经过编译后都是List,jvm是看不到泛型的。
    所以反射这种基于字节码的动态特性是没办法跟泛型扯上关系的。

    打赏 评论
  • ll89308839 2012-03-23 17:29

    虽然对于java来说泛型再编译的时候被擦除了,但是始终还是能拿到它的信息的
    [code="java"]
    public class A extends B
    protected Class entityClass;

    public A() {

    entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
    [/code]

    打赏 评论
  • Aronlulu 2012-03-23 17:50

    [quote]虽然对于java来说泛型再编译的时候被擦除了,但是始终还是能拿到它的信息的 [/quote]
    都擦除了还怎么拿,你看清楚你拿到的是什么信息,E才是想要的,而不是强制转换的内部变量。都知道是Class类型了,当然拿得到。
    去看看C++的模板类,就知道什么是真正的泛型了。

    打赏 评论
  • bukebuhao 2012-03-23 19:45

    反射实例化一个抽象类,应该是不可的,因为反射只是一种语法约束而已,可利用内部类实例化匿名类。例如 new MyClass { //实现抽象方法}

    打赏 评论
  • ll89308839 2012-03-23 20:51

    好吧,楼主你就自己看吧,我也不说什么了,拿得到拿不到楼主看了就明白了
    [url]http://www.iteye.com/topic/585900[/url]

    打赏 评论
  • Aronlulu 2012-03-23 21:04

    [code="java"]好吧,楼主你就自己看吧,我也不说什么了,拿得到拿不到楼主看了就明白了 [/code]
    你先看清楚吧,不要误导了LZ。
    MyClass类中的“T”你能拿到?
    说了 类型擦除,T永远是被擦除掉的,反射只能操作字节码,字节码中有的就能获取,T属于泛型,字节码中没有。
    new MyClass这个是泛化的么?文章中的方式就是获取这种非泛化类型,毫无意义,所以说不要把反射跟泛型扯在一起,只会让人误导java的泛型有多强大似的。
    说了,只能用来做编译检查。

    打赏 评论
  • bukebuhao 2012-03-24 19:43

    还以为要实例化一个抽象类的,不过,楼主为何要这样做呢?转换成一个泛型类,然后利用泛型简繁重复代码,泛型适用的情况,只是为了更好的编译,减少类型转换的错误。为何不考虑一下设计模式,转换成命令模式,回调方法不是一样可以实现效果

    打赏 评论
  • Aronlulu 2012-03-24 23:44

    [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]
    这就是为什么像命令模式了,你告诉他什么类型,然后就执行什么类型的方法。

    打赏 评论
  • bukebuhao 2012-03-25 18:45

    按照我目前的理解,可能有错,仅供参考,你只要把json转换出来的bean包装成一个命令,无论是list还是普通的bean都转换成一个通用的命令,然后写一个通用的执行方法,执行命令,获取执行结果,无论以后从json获取的东西怎么变,执行命令的方式获取执行结果都不会受影响,你也可参考 [url]http://www.iteye.com/topic/1121817[/url]

    打赏 评论
  • Aronlulu 2012-03-26 10:05

    [quote]通过方法名和参数个数做第一层过滤,然后直接使用符合条件的第一个方法的参数转换json,如果没有抛异常,认为调用方法正确,如果抛异常了,就尝试用下一个方法的参数转换。
    这样基本可以解决我提出的问题,但是对于参数个数相同,类型不同的重载方法,存在效率问题。但是如果这种类型的重载方法很少,也可以勉强接受。还好,我们的系统只有一个方法存在这样的问题,而且调用频率极低,有时间做个测试,看看效率差多少。[/quote]
    你还不如对你传进去的json做过滤呢,看这种json格式是需要转成list的还是string的还是map的。无非就是校验格式。看看嵌套几层。校验完成后就可以回调相应的方法。
    反射遍历,通过反射获取的method又没有缓存,效率会很低,而且还把运行时异常作为逻辑来使用,不太合适。
    写个枚举类,写个校验方法,输入的json字符串,然后输出对应的格式枚举类型,然后直接调用该枚举类型的转换方法。
    以后要是新增转换类型,无非就是新加个枚举,这跟你新写个重载方法代码量差不多。

    打赏 评论
  • bukebuhao 2012-03-26 14:28

    按照楼主的思路去考虑,感觉最好要有一个标记来映射json数据和调用具体的方法,这样就不用去一个个方法去尝试了。现在很多流行的对外开放平台,都是能直接映射方法的。

    打赏 评论
  • Aronlulu 2012-03-26 18:18

    这个类型本身就是你们系统定义的,当然得自己写代码的时候传。
    就一个普通的字符串:{'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]

    打赏 评论
  • Aronlulu 2012-03-26 18:21

    晕,测试后发现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]

    打赏 评论
  • bukebuhao 2012-03-26 18:50

    [quote]aronlulu 的方式是可取的,不过,lz的意思现在是如何关联json和具体的实现类[/quote],[quote]其实客户端是知道请求参数类型的,完全可以传过来,但是我们的系统是开放给第三方的,在webservice中包一层json,第三方可能是Java也可能是C#的或者别的语言,所以让C#的客户端传个Java参数的类型标记,总觉得说不通。[/quote] 为何一定要传个Java参数的类型标记,任何一个有意的标记,通过map自动映射过去不一样啦。例如现在spring3 mvc都是通过url和方法自动映射的

    打赏 评论
  • bukebuhao 2012-03-26 21:34

    讨论到这种程度,已经很不错了,lz也已经有了可选的方案了。希望以后多多交流

    打赏 评论
  • ghc123 2017-08-02 13:42

    [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]
    
    打赏 评论

相关推荐 更多相似问题