接口中使用泛型参数的思考

今天在使用一个别人提供的接口时,发现该接口的定义如下:

1
2
3
4
public interface xxxService<T extends BasicModel> {
    public T getXXX();
    public T countXXX();
}

看到上面的接口,我们发现这个接口需要一个泛型参数,同时这个泛型参数必须是BasicModel或者BasicModel的派生类型。同时我们也应该想到xxxService可能会有多种实现,因为不同的泛型参数对应不同的实现。比如

1
2
3
4
5
6
7
8
9
10
11
12
public class xxAServiceImpl implements xxxService<A extends BasicModel> {
    public A getXXX() {
        A a = xxx;
        ...
        return a;
    }
    public A countXXX() {
        A a = xxx;
        ...
        return a;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
public class xxBServiceImpl implements xxxService<B extends BasicModel> {
    public B getXXX() {
        B b = xxx;
        ...
        return b;
    }
    public B countXXX() {
        B b = xxx;
        ...
        return b;
    }
}

这样可以提供不同粒度的服务化接口的实现,但是有一个统一定义的地方,可以说这个泛型的接口是一个高度的抽象。

此时你可能会考虑到Spring的注入,针对这种泛型接口,Spring的注入也没有啥问题,基于上面的实现,我们可以写下面的spring配置文件

1
2
<bean id="xxAService" class="xx.xx.xxAServiceImpl" />
<bean id="xxBService" class="xx.xx.xxBServiceImpl" />

此时我们要是在其他类中按照名称注入这两个bean

1
2
3
4
5
public class xxxService {
    private xxxService<A> xxAService;
    private xxxService<B> xxBService;
    // 这里省略setter方法,或者你可以使用@Resource(name="xxx")来按照名称注入
}

这样注入是没有任何问题的。
但是要是想按照类型注入,那就出现注入不了的问题

1
2
3
4
5
6
public class xxxService {
    @Autowired
    private xxxService<A> xxAService;
    @Autowired
    private xxxService<B> xxBService;
}

此时xxAService和xxBService在Spring容器中的类型都是xxxService,因此注入的时候会报错,说找不到唯一可注入的bean。

因此带有发泛型参数的bean在Spring中没法按照类型注入,只能按照name注入,泛型参数经过javac后被擦除掉了,因此Spring也没法区分这二者的类型。

看到extends,我们也能想到super, ? super T也是一个类型,这个类型要么是T要么是T的超类。