转自:http://codelife.me/blog/2014/03/05/returning-subtype-of-declared-type-from-method-by-java-generics/
泛型典型的使用场景是集合。考虑到大多数情况下集合是同质的(同一类型),通过声明参数类型,可免去类型转换的麻烦。本文将讨论本人阅读Spring Security源码时遇到的一个关于泛型递归模式的问题。
在Spring Security的源码里有一个ProviderManagerBuilder接口,声明如下:
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> extends
SecurityBuilder<AuthenticationManager> {
B authenticationProvider(AuthenticationProvider authenticationProvider);
}
其实现类AuthenticationManagerBuilder
public class AuthenticationManagerBuilder extends
AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder> implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
//...
public AuthenticationManagerBuilder authenticationProvider(
AuthenticationProvider authenticationProvider) {
this.authenticationProviders.add(authenticationProvider);
return this;
}
//...
}
上面有很多干扰项,我们来简化一下
接口A定义如下
public interface A<T extends A<T>> {
T add();
}
A接口的实现类B
public class B implements A<B> {
@Override
public B add() {
return null;
}
}
/*注意,此处类`B`里的add方法返回类型`B`。也就是说,
接口`A`里声明的方法时并不知道子类型`B`的存在,通过继承和泛型,
可以放返回值动态的适配子类型,这一切都要归功于`<T extends A<T>>`
*/
泛型递归模式(Recurring Generic Pattern)
public interface A<T extends A<T>>
对于参数类型T是递归定义的。有如GNU的定义“GNU’s Not Unix!”。
典型的例子是java.lang.Enum
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
//...
}
java所有的枚举类型都隐式的继承java.lang.Enum
,不允许通过现实的继承声明枚举类型,甚至集成java.lang.Enum
也是编译器所不允许的。
假设有一个枚举类StatusCode,其等价的声明如下:
public class StatusCode extends Enum<StatusCode>
现在我们来验证一下泛型约束,
1.因为Enum<StatusCode>
,所以E=StatusCode
2.根据<E extend Enum<E>>
和 E=StatusCode
可得,<StatusCode extend Enum<StatusCode>>
3.由于public class StatusCode extends Enum<StatusCode>
第二步的结论显然成立。
为什么Enum的声明这么绕?直接Enum不行么?
因为Enum<E>
实现了Comparable<E>
接口,该接口有一个compareTo
方法:
public int compareTo(E o) {}
泛型递归模式与继承
泛型递归模式interface A<T extend A<T>>
用于约束参数类型T
,要求其为类型A
的子类。考虑到继承和实现B implements A<B>
,参数类型和实体类型是一致的。这样类A
中方法签名里涉及到参数类型T
的地方,在实现类里会为实现类本身,这让类型系统更加的严谨。