LinkedTreeMap cannot be cast , JAVA继承中泛型类型推断问题

最近做配置表soa的时候,想提供增删改查基础序列化父类,遇到JAVA泛型在继承中的问题。具体如下:

public class Test {
    public class Node {
        public String key;
        public String value;
        public Node() {
            key = "aa";
            value = "bb";
        }
    }

    public class AbstractDbService {
        public List getListFromGson(String str) {
            List result = null;
            Type type = new TypeToken>(){}.getType();   //执行失败,此处T未真正转型
//          Type type = new TypeToken>(){}.getType(); //可以正确执行
            try {
                result = new Gson().fromJson(str, type);
            } catch (Exception e) {}
            return result;
        }
    }

    public class NodeDbService extends AbstractDbService {
        public List getListFromGson(String str) {
            return super.getListFromGson(str);
        }
    }

    public void test() {
        NodeDbService nodeDbServiceType = new NodeDbService();
        List list = new ArrayList();
        list.add(new Node());
        String str = new Gson().toJson(list);
        List result = nodeDbServiceType.getListFromGson(str);
        for(Node node : result) {
            System.out.println(node);
        }
    }

    public static void main(String args[]) {
        Test test = new Test();
        test.test();
    }
}

NodeDbService在继承实现的时候,定义了具体类型,父类定义了反序列化的基本方法。test应该可以正常执行,但是却报错了:


注意父类里面的注释行,如果指定了T类型为Node,则可以正常执行。说明在运行到父类方法的时候,此处的T并未真正转型。如果在其他真正支持泛型的语言里,如C#,上述代码是可以正确执行的。原因是JAVA实现的泛型是伪泛型。jdk1.5以后,在class文件里,method(List list)和method(List list)方法描述是一样的,只是额外新增了方法签名(Signature)和LocalVariableTypeTable保存对应方法的泛型信息。只有在赋值操作时才用到泛型信息,用法只是做了下强制转换而已。这也是通常所说的类型擦除。在整个父类方法执行的过程中,是不知道泛型信息的,而成功反序列化依赖外部调用方法的泛型信息。在不知道类型的情况下,只能解析为Object, Gson 用Map存储这类结果,就是报错内容。父类嵌套调用比较隐蔽,改变下实现方式:

public class Test {
    public class Node {
        public String key;
        public String value;
        public Node() {
            key = "aa";
            value = "bb";
        }
    }

    public class Single {
        public  List getListFromGson(String str) {
            List result = null;
            Type type = new TypeToken>(){}.getType();
            try {
                result = new Gson().fromJson(str, type);
            } catch (Exception e) {}
            return result;
        }

        public List wrap(String str) {
            List result = getListFromGson(str);
            return result;
        }
    }

    public void test2() {
        List list = new ArrayList();
        list.add(new Node());
        String str = new Gson().toJson(list);
        List result = new Single().wrap(str);
        for(Node node : result) {
            System.out.println(node);
        }
    }

    public static void main(String args[]) {
        Test test = new Test();
        test.test2();
    }
}

上述代码只是在泛型方法上加了一个嵌套方法。因为=操作才执行泛型转换,所以在getListFromGson(str)执行过程中,都没有Node对应的信息,TypeToken依赖T类型,所以执行失败。

正确的做法是,如果依赖调用方法的泛型信息,就主动传入。在继承中的话,定义工厂方法,由子类返回泛型信息,具体如下:

public class Test {
    public class Node {
        public String key;
        public String value;
        public Node() {
            key = "aa";
            value = "bb";
        }
    }

    public abstract class AbstractDbService {
        abstract Type getType();
        public List getListFromGson(String str) {
            List result = null;
            try {
                result = new Gson().fromJson(str, getType());
            } catch (Exception e) {}
            return result;
        }
    }

    public class NodeDbService extends AbstractDbService {
        @Override
        Type getType() {
            return new TypeToken>(){}.getType();
        }

        public List getListFromGson(String str) {
            return super.getListFromGson(str);
        }
    }

    public void test() {
        NodeDbService nodeDbServiceType = new NodeDbService();
        List list = new ArrayList();
        list.add(new Node());
        String str = new Gson().toJson(list);
        List result = nodeDbServiceType.getListFromGson(str);
        for(Node node : result) {
            System.out.println(node);
        }
    }

    public static void main(String args[]) {
        Test test = new Test();
        test.test();
    }
}
这样就可以正常调用了。


你可能感兴趣的:(java,泛型,类型推断,继承)