Scala类型边界与方差:Hub类类型修正方案及原理问询
Got it, let's work through this problem step by step. First, let's recap the requirements to make sure we're aligned:
We have this type hierarchy:
class A class B extends A class C extends A class D extends C
And we need to modify the initial Hub class so that the following code is valid:
val a : Hub[D] = Hub(new D) val b : Hub[A] = a.add(new B) val c : Hub[A] = b.add(new C) val d : Hub[A] = c.add(new D)
But this line must fail to compile:
val e : Hub[C] = b.add(new C)
The Modified Hub Implementation
Here's the corrected Hub class definition that meets all requirements:
class Hub[+T](val elem: T) { def add[U >: T](newElem: U): Hub[U] = new Hub(newElem) } // Companion object to match the example's clean construction syntax object Hub { def apply[T](elem: T): Hub[T] = new Hub(elem) }
Why This Works: Scala Type Principles Explained
Let's break down the key type features we're leveraging here:
1. Covariant Type Parameter (+T)
By marking the type parameter T with +, we declare that Hub is covariant in T. This means if S is a subtype of T (written S <: T), then Hub[S] is a subtype of Hub[T].
- This lets
val a: Hub[D] = Hub(new D)work as expected, and more importantly, it allows us to assign the result ofa.add(new B)(which isHub[A]) toval b: Hub[A]—sinceHub[D]is a subtype ofHub[A]. - Critically, covariance ensures
val e: Hub[C] = b.add(new C)fails:bis aHub[A],add(new C)returnsHub[A], and sinceAis a supertype ofC,Hub[A]is a supertype ofHub[C]. Scala doesn't let you assign a supertype to a subtype variable, hence the compile error we want.
2. Lower Bound Constraint (U >: T)
The add method uses a type parameter U with a lower bound >: T, meaning U must be a supertype of T (or T itself).
- This solves a core limitation of covariant types: you can't use a covariant type parameter directly as a method parameter (it would break type safety). By using a lower bound, we ensure any element we add is at least as general as the current
Hub's type, so the resultingHub[U]is a valid supertype of the originalHub[T]. - When we call
a.add(new B)(on aHub[D]), Scala infersUto beA—the least upper bound ofDandB—so the method returnsHub[A], which fits perfectly intoval b: Hub[A]. Forb.add(new C)(on aHub[A]),Uis inferred asA(sinceAis already the supertype ofC), returningHub[A]which can't be assigned toHub[C].
Verification of Requirements
Let's confirm each case:
- ✅
val a : Hub[D] = Hub(new D): Compiles (direct construction ofHub[D]). - ✅
val b : Hub[A] = a.add(new B): Compiles (returnsHub[A], compatible with the variable type). - ✅
val c : Hub[A] = b.add(new C): Compiles (returnsHub[A]). - ✅
val d : Hub[A] = c.add(new D): Compiles (returnsHub[A]). - ❌
val e : Hub[C] = b.add(new C): Fails to compile (invalid supertype-to-subtype assignment).
内容的提问来源于stack exchange,提问作者Егор Лебедев

