一、对照速查(最常用语法一眼记住)
| 场景 | Java 语法 | Kotlin 语法 | 备注 | 
|---|---|---|---|
| 泛型类/接口 | class Box { T v; } | class Box(val v: T) | T 属于类型本身 | 
| 泛型方法/函数 | static T id(T x) | fun id(x: T): T | Java 静态方法必须自己声明 | 
| 上界(单) | ":`" 等同 extends | ||
| 多重上界 | <T extends Number & Serializable> | where T : Number, T : Serializable | Kotlin 用 where | 
| 通配/投影:读 | List<? extends Number> | MutableList / List | 生产者 Extends/out | 
| 通配/投影:写 | List<? super Integer> | MutableList | 消费者 Super/in | 
| 通配星号 | 无 | List<*> | "未知 T 的只读视图" | 
| 原始类型 | List raw = ... | 无(不推荐) | Java 兼容旧代码 | 
| 创建数组 | 不能 new T[] | 不能 arrayOf(...)(需具体类型) | 擦除所致 | 
| 类型推断 | new HashMap<>()(diamond) | val m = hashMapOf<K,V>() | 目标类型参与推断 | 
| reified | 无 | inline fun ... | 运行时拿到 T | 
二、声明与使用
1) 泛型类/接口
            
            
              csharp
              
              
            
          
          class Box<T> { private T v; T get(){return v;} void set(T x){v=x;} }
            
            
              kotlin
              
              
            
          
          class Box<T>(var v: T)2) 泛型方法/函数
            
            
              javascript
              
              
            
          
          static <T,R> R map(T x, Function<T,R> f) { return f.apply(x); }
            
            
              kotlin
              
              
            
          
          fun <T, R> map(x: T, f: (T) -> R): R = f(x)静态上下文看不到"类上的 T",所以 Java 的静态方法要自己写 。Kotlin 顶层函数天然"静态",直接写 即可。
三、上界与多重约束(含递归约束)
单/多上界
            
            
              javascript
              
              
            
          
          <T extends Number>                    // 单
<T extends Number & Serializable>     // 多(第一个必须是类,其余是接口)
            
            
              kotlin
              
              
            
          
          <T : Number>                          // 单
fun <T> f(...): T where T: Number, T: Serializable  // 多递归约束(自限定类型)
            
            
              scala
              
              
            
          
          class Node<T extends Comparable<T>> { ... }
            
            
              csharp
              
              
            
          
          class Node<T> where T : Comparable<T>四、变型(协变/逆变):PECS 一句记住
PECS :P roducer E xtends / C onsumer Super
生产者(只读)用 extends/out;消费者(只写)用 super/in。
Java:use-site wildcard
            
            
              javascript
              
              
            
          
          void copy(List<? extends Number> src, List<? super Number> dst) {
  for (Number n: src) dst.add(n);   // src 只能读、dst 只能写
}Kotlin:声明处与使用处
- 声明处变型(在类型参数上标注,影响所有使用点)
            
            
              kotlin
              
              
            
          
          interface Source<out T> { fun get(): T }   // 协变:只能"产出"T
interface Sink<in T>   { fun put(x: T) }   // 逆变:只能"消费"T- 使用处投影(临时把某实例视为只读/只写)
            
            
              kotlin
              
              
            
          
          fun readAll(xs: MutableList<out Number>) { val n: Number = xs[0] }
fun writeAll(xs: MutableList<in Number>) { xs.add(1); val a: Any? = xs[0] }常见集合差异
- 
Java :List 不变;协变靠 ? extends。 
- 
Kotlin :List(只读接口协变);MutableList 不变(需要 in/out 投影)。 
函数类型(天然带变型)
            
            
              css
              
              
            
          
          // (P) -> R 实际是 Function1<in P, out R>
val f: (Number) -> Any = { it }  // 参数逆变、返回协变五、星投影与原始类型
- 
Kotlin 星投影 List<*> 表示"某种 T 的 List,但我不知道 T 是什么";可以读到 Any?,不可写入除了 null。 
- 
Java 原始类型 List raw = ... 兼容旧代码;绕过类型检查,读写都不安全(会有 ClassCastException 风险)。能不用就别用。 
六、类型擦除与那些限制
- 
JVM 泛型大多被擦除:运行时看不到 List 的 String。 
- 
直接后果: - 
不能 new T() / new T[]; 
- 
不能 instanceof List(只能 instanceof List<?>); 
- 
Kotlin 里不能 is T(除 reified 场景)。 
 
- 
解决路径
- 显式传"类型令牌" :Class / Type(Java)、KClass / KType(Kotlin)。
- Kotlin reified(见下一节)。
- 从"声明处签名"反射:字段/方法上如果写死了 List,可反射拿到;若是 List 只能得到 TypeVariable。
七、Kotlin 的 reified(拿到"真实 T")
            
            
              kotlin
              
              
            
          
          inline fun <reified T> parse(json: String): T {
  // 可以做 T::class、is T、typeOf<T>() 等
}
val users: List<User> = parse(json)- reified 只能用于 inline 函数的类型参数;编译器把 T 的具体类型写进调用点。
- 典型用法:filterIsInstance()、序列化/反序列化、反射工具。
八、更多语法与实务小贴士
Java 专属
- 
菱形语法:new HashMap<>()。 
- 
通配符捕获:写辅助方法把 List<?> 捕获为 。 
- 
可变参数 + 泛型:用 @SafeVarargs 或 @SuppressWarnings("varargs") 配合,避免堆污染告警。 
Kotlin 专属
- 
默认类型实参:泛型类/接口可给默认类型参数:class Api<T: Any = Unit>。 
- 
交叉类型(intersection) :T 同时满足多个上界时,推断类型可能是 A & B。 
- 
空安全:List<String?> vs List?;读写时分清"元素可空"与"容器可空"。 
Java ↔ Kotlin 互操作
- 
通配与通配抑制: - Kotlin 调 Java:可能出现 MutableList 的签名 → 需要 @JvmSuppressWildcards/@JvmWildcard 调整生成的字节码通配符,便于 Java 端调用。
 
- 
平台类型:从 Java 来的类型在 Kotlin 里是 String!,要自己做好空值与泛型边界的防守。 
九、常见坑与最佳实践
- PECS 记不住? 背口诀:生产者 Extends/out,消费者 Super/in。
- 把可变集合投成 out 还想写 → 不行;out 基本变只读。
- 可变对象当 key(与泛型无关但常同场出现):修改后取不回,因为 hashCode/equals 改了。
- 滥用原始类型:用 List 代替 List 会把类型错误推迟到运行时。
- new T[]/Array 限制:用具体元素 Class 创建:
            
            
              ini
              
              
            
          
          @SuppressWarnings("unchecked")
T[] arr = (T[]) java.lang.reflect.Array.newInstance(componentClass, n);十、综合示例(两端各一段,覆盖高频语法)
Java:拷贝 & 过滤
            
            
              javascript
              
              
            
          
          static <T> void copy(List<? extends T> src, List<? super T> dst) {
  for (T e : src) dst.add(e);
}
static <T extends Number & Comparable<T>>
List<T> topK(List<T> xs, int k) {
  xs.sort(Comparator.reverseOrder());
  return xs.subList(0, Math.min(k, xs.size()));
}Kotlin:只读/只写投影 + reified
            
            
              kotlin
              
              
            
          
          fun <T> copy(src: List<out T>, dst: MutableList<in T>) {
  for (e in src) dst.add(e)
}
inline fun <reified T> Iterable<*>.only(): List<T> =
  this.filterIsInstance<T>()     // 调用点"实化"T
// 多重约束
fun <T> maxOf(a: T, b: T): T where T: Number, T: Comparable<T> =
  if (a >= b) a else b一句话收束
Java:不变 + ? extends/? super 做使用处变型;靠 Class/Type 传"类型令牌"。
Kotlin :声明处 out/in + 使用处投影、List 协变;inline + reified 能在运行时拿到 T。
牢记 PECS、上界/多界规则与擦除限制,你就能把 Java & Kotlin 的泛型写得既安全又优雅。