return 存在类型有意义吗?

Does it make sense to return an existential type?

如果我定义一个 return 存在类型的方法:

trait X[T] {
  def hello(x: T):Unit = println(x)
}

def myX: X[_] = new X[Any] {}

如果我调用 myX,我将得到一个类型为 X[_] 的值,但我无法向它传递任何内容:

val y: Any = 123
myX.hello(y)  // compilation error!

我唯一能做的就是传递一个 Nothing:

val y: Nothing = ???
myX.hello(y)  // compilable, but useless

所以return存在类型的方法没有意义?

scala> trait X[T] {
     |   def hello(x: T):Unit = println(x)
     | }
defined trait X

你定义的函数:

scala> def myX: X[_] = new X[Any] {}
myX: X[_]

scala> val xnothing = myX
xnothing: X[_] = $anon@5c44c582

scala> xnothing.hello(1)
<console>:14: error: type mismatch;
 found   : Int(1)
 required: _
       xnothing.hello(1)
                      ^

"Fixed" 相同函数的定义 - 正确捕获类型 arg:

scala> def myY[T]: X[T] = new X[T] {}
myY: [T]=> X[T]

如果未传递通用 arg,则推断 Nothing

scala> val ynothing = myY
ynothing: X[Nothing] = $anon@53f0a4cb

您可以显式传递通用参数。

scala> val yint = myY[Int]
yint: X[Int] = $anon@555cf22

显然 Int 不是 Nothing

scala> ynothing.hello(1)
<console>:14: error: type mismatch;
 found   : Int(1)
 required: Nothing
       ynothing.hello(1)
                      ^

这显然有效,因为我们明确地传递了 Int

scala> yint.hello(1)
1

现在这是一个有趣的部分。它之所以有效,是因为 Scala 可以计算出通用 arg 是 Int(根据用法)。将其与上面的 ynothing 定义及其不编译的调用 ynothing.hello(1) 进行对比。

scala> myY.hello(1)
1

return this 存在类型没有多大意义,但它们在其他情况下可能很有用。作为一个简单的例子

// method which returns an existential type
// note that you can't return Array[Any] here,
// because arrays aren't covariant in Scala (which is a good thing)
def array: Array[_] = if (some condition) Array[Int](...) else Array[Double](...) 

// all of following works
array(0)
array.length
// etc.

当然,如果你能写出一个方法的精确泛型类型,那你肯定更喜欢。