首页 > Java的反射 和 泛型的一个问题

Java的反射 和 泛型的一个问题

大概摘要一些在这里

Class clazz = this.getClass();
UpdateOperations up = ds.createUpdateOperations(clazz);
ds.update(ds.createQuery(clazz).filter("_id",id), up);

完整的在 https://github.com/mongodb/morphia/issues/542

mophia是一个mongodb 的ORM框架,然后我在一个基类里写了一个update方法, 希望可以动态根据子类类型,利用反射完成一些更新任务。

但是morphia的update方法有两个泛型方法:

T update(T,UpdateOperations<T>  

T update(Query<T>,UpdateOperations<T>)

我在用ant编译的时候报错了,提示说 ds.update(ds.createQuery(clazz).filter("_id",id), up); 匹配了以上2个方法,有歧义。 最奇怪的是eclipse调试什么的都没问题,只是一个unchecked的警告而已:

Type safety: Unchecked invocation update(Query, UpdateOperations) of the generic method update(Query, UpdateOperations) of type Datastore

而且可以从警告看出,是匹配了上面第二个update方法。

不知道应该怎么解决,是不是编译的时候要设置一些参数呢?


模板方法在编译过程中类型擦除:

  1. update(T,UpdateOperations) 退化成 update(Object,UpdateOperations);
  2. update(Query,UpdateOperations) 退化成 update(Query,UpdateOperations);

morphia的update方法不光是一个模板方法,还是一个重载方法。重载方法是在编译时确定具体调用哪个方法,由于你在传参的时候,没有传入类型参数:

  1. up,是一个没有带模板参数的原始类型UpdateOperations;
  2. ds.createQuery(clazz).filter("_id",id),同样是一个没有带模板参数的原始类型Query;

由于Query可以同时匹配Query和Object类型,所以会造成编译时同时匹配这2个update方法,无法确定具体调用哪个重载方法。

解决办法:

给个简单示例:

(javac无法编译通过)

public class AppTest {

public static void main(String[] args) {
    Print p = new Print();
    AppTest app = new AppTest();
    Class clazz = AppTest.class;
    app.print(clazz, p);
}

private <T> void print(T obj, Print<T> p) {
    System.out.println("type T");
}

private <T> void print(Class<T> clazz, Print<T> p) {
    System.out.println("type Class<T>");
}

static class Print<T> {
    Print() {}

    void p() {}
}
}

(javac编译ok)

public class AppTest {

    public static void main(String[] args) {
        Print<AppTest> p = new Print<AppTest>();
        AppTest app = new AppTest();
        Class<AppTest> clazz = AppTest.class;
        app.print(clazz, p);
    }

    private <T> void print(T obj, Print<T> p) {
        System.out.println("type T");
    }

    private <T> void print(Class<T> clazz, Print<T> p) {
        System.out.println("type Class<T>");
    }

    static class Print<T> {
        Print() {}

        void p() {}
    }
}

PS:eclipse的编译级别放的比较宽,有些unchecked的都直接给Pass掉了,这个能否在eclipse中设置,我没试过,你可以自行研究看看,但是通过javac编译不通过肯定是有问题滴...


谢谢 @halty 的解释,知道类型擦除导致歧义。

我经过修改之后改成如下形式,编译成功了:

Class clazz = this.getClass();
Query<DomainBase> query = ds.createQuery(clazz).filter("_id",id);
UpdateOperations<DomainBase> up = ds.createUpdateOperations(clazz);
ds.update(query, up);

之前的思路不对,一直想着怎怎么获取子类的类型,其实这个update方法里面,类型真的没有什么作用,只是利用传入参数clazz这个Class类型的对象来反射来解析Domain对象的字段而已,而Query<?> 中是什么类型根本无关紧要,所以我们只要显示指定一个类型,让编译器能明确无歧义匹配上第二个方法就可以了。按照这样的思路我显示指定模板为 基类DomainBase,这样就可以明确匹配第二种update方法了。

我想就算我把Query<DomainBase> 改成 Query<Object> 甚至 Query<String> 都没问题。

【热门文章】
【热门文章】