1) 界类型参数(上界 / 多重上界 / 下界通配)
1.1 上界(Upper Bounds)
目的:限制类型实参必须"至少是某个类型的子类型",从而在方法体内能安全调用上界的方法/属性。
- Java
javascript
<T extends Number> T sum(T a, T b) { ... } // T 至少是 Number
<T extends Number & Comparable<T>> T max(Collection<T> xs) { ... } // 多重上界
- 规则:多个上界用 &,第一个若为类,其余必须是接口。
- Kotlin
kotlin
fun <T : Number> sum(a: T, b: T): T = ...
fun <T> max(xs: Collection<T>): T
where T : Number, T : Comparable<T> = ...
- 多重上界用 where 子句;语义与 Java 一致。
何时用
-
想在泛型算法里用到特定 API(如 compareTo、close()、length())。
-
经典:Collections.max 的签名在上界约束里就要求 "T 可比较"。
1.2 下界(Lower Bounds)
注意 :Java/Kotlin 的"类型参数"只支持上界 ;"下界"出现在通配符/投影上:
-
Java wildcard(使用处)
- 读:? extends T(生产者,只读)
- 写:? super T(消费者,只写)
javascript
void copy(List<? super T> dst, List<? extends T> src) { ... }
-
Kotlin 使用处投影
- out T ≈ ? extends T,in T ≈ ? super T
kotlin
fun <T> copy(src: List<out T>, dst: MutableList<in T>) { ... }
2) 递归边界(F-bounded Polymorphism / 自递归约束)
本质 :让类型参数在上界里引用自己 ,确保"同类比较/同类构建"等自一致性。也叫 CRTP(Curiously Recurring Template Pattern) 。
2.1 经典示例
-
Comparable:只比较同类型
- Java:interface Comparable { int compareTo(T other); }
- 你的类型:final class Money implements Comparable { ... }
-
Enum 的自约束
scala
abstract class Enum<E extends Enum<E>> implements Comparable<E> { ... }
- 确保 Color.compareTo(...) 的参数是 同一个枚举类型。
- Kotlin 等价
kotlin
class Money(...) : Comparable<Money> { override fun compareTo(other: Money): Int = ... }
class Node<T : Node<T>>(val children: List<T>)
2.2 Fluent Builder 的"自返回类型"
避免子类链式 API 返回父类类型而需要强制转换:
- Java
scala
abstract class Self<T extends Self<T>> {
@SuppressWarnings("unchecked")
protected T self() { return (T) this; }
public T name(String n) { /*...*/ return self(); }
}
final class UserBuilder extends Self<UserBuilder> {
public UserBuilder age(int a) { /*...*/ return this; }
}
- Kotlin
kotlin
abstract class Self<T : Self<T>> {
@Suppress("UNCHECKED_CAST")
protected fun self(): T = this as T
fun name(n: String): T = self()
}
class UserBuilder : Self<UserBuilder>() {
fun age(a: Int): UserBuilder = this
}
使用场景
-
比较器/枚举/节点树等"同类交互"。
-
Fluent API/Builder 保持子类链式返回正确类型。
易错点
- 递归上界不是 协变:T extends Base 不会让 T 之间产生子类型关系;只是约束"自己与自己"一致。
- 小心层层继承导致的复杂度;必要时用组合代替继承。
3) 交叉类型(Intersection Types,A & B)
含义 :同时满足多个类型约束的类型;编译器视为"既是 A 又是 B"。
在 Java 里主要出现在类型参数上界 与交叉类型强制转换 ;在 Kotlin 里以 where 多上界出现,也可作为类型推断结果(写作 A & B 的抽象类型,不常显式书写)。
3.1 在"上界"声明交叉类型
- Java
java
static <T extends Closeable & Runnable> void use(T x) throws IOException {
x.run(); x.close();
}
- Kotlin
kotlin
fun <T> use(x: T) where T : Closeable, T : Runnable {
x.run(); x.close()
}
3.2 交叉类型强制转换(Java 独有的显式 cast)
ini
Object o = ...;
Runnable r = (Runnable & Closeable) o; // 同时视作二者
常与动态代理/混入(mixin)配合:生成一个同时实现多接口的对象,再用交叉转换获得"多接口视图"。
3.3 Kotlin 的"推断得到交叉类型"
kotlin
fun useBoth(x: Any) {
if (x is Runnable && x is Closeable) { // 智能类型判断
// 这里 x 的静态类型被推断为 Runnable & Closeable
x.run(); x.close()
}
}
注意 :交叉类型是编译期概念;运行时仍是擦除后的类。你不能"创建一个 A & B 的实例",只能创建实现了这两个接口/继承结构的实例。
4) 三者如何组合到"集合/算法签名"里
4.1 上界 + 交叉:要求元素可比较、可关闭
- Java
scss
static <T extends Comparable<? super T> & Closeable>
T maxAndClose(Collection<? extends T> xs) throws IOException {
T best = xs.stream().max(Comparator.naturalOrder()).orElseThrow();
best.close();
return best;
}
- Kotlin
kotlin
fun <T> maxAndClose(xs: Collection<out T>): T
where T : Comparable<T>, T : Closeable {
val best = xs.maxOrNull() ?: error("empty")
best.close()
return best
}
4.2 自递归约束 + PECS:拷贝并排序
- Java
javascript
static <T extends Comparable<? super T>>
void sortInto(List<? extends T> src, List<? super T> dst) {
List<T> tmp = new ArrayList<>(src);
Collections.sort(tmp); // T 可比较
dst.addAll(tmp); // super: 写入父类型容器
}
- Kotlin
kotlin
fun <T> sortInto(src: List<out T>, dst: MutableList<in T>)
where T : Comparable<T> {
val tmp = src.toMutableList()
tmp.sort()
dst.addAll(tmp)
}
5) 常见误区与排坑
-
把下界写到类型参数上
- class Box(Java)/不存在;类型参数只有上界。
- 下界只在通配符/投影:? super Foo / in Foo。
-
认为递归上界带来子类型传递
- class A<T extends A> 与 A**/ A 之间没有子类型关系,它只是"自一致"的约束。**
*** 交叉类型=联合类型?
- 不是 。交叉 A & B 表示"同时是 A 和 B";联合 A | B(Java/Kotlin 均不支持)表示"是 A 或 B"。
- 多重上界顺序随意?
- Java 中若包含类上界,它必须在最前;其余是接口:<T extends Class & I1 & I2>。
- 擦除导致的运行时限制
- instanceof List、new T[] 都不行。需要 类型令牌 (Class/Type、Kotlin KType)或 inline + reified(Kotlin)辅助。**
6) 选型与写法速查
- 只是要用到一些 API → 给 上界: / fun <T: Closeable> ...
- 要求同时满足多个接口 → 交叉上界:<T extends A & B> / where T: A, T: B
- 链式/子类保类型 → 递归上界(CRTP)
- 集合只读/只写 → 用 PECS/投影:? extends T(读) / ? super T(写)
- 返回值类型 → 一般避免通配与过多上界,把复杂性留在参数与约束上
一句话收束
界类型参数 用"上界/多上界"把可用 API 锁定;递归边界 保证"同类交互/自返回"保持类型一致;交叉类型 让一个类型同时满足多接口/多约束。三者配合 PECS(extends/out 与 super/in) 使用在集合与算法签名里,就能写出既类型安全 又表达力强的泛型 API。**