如何使 Scala 类型参数推断更智能?

How to make scala type parameter inference smarter?

我有一个函数接受类型参数 T,它必须是 this.type 的子类型:

def injectFrom[T <: this.type](same: T): Unit = ...

然而每次我使用它时,编译器都会给我一个

type T cannot be inferred" error

并且每次都可以通过将 T 明确指定为 (instance_variable).type 来修复。我以后如何避免这样做?

编译器坚决拒绝推断单例类型。

scala> class C { def f[A <: this.type](a: A) = ??? }
defined class C

scala> val c = new C
c: C = C@71f2a7d5

scala> c f c
<console>:10: error: inferred type arguments [C] do not conform to method f's type parameter bounds [A <: c.type]
              c f c
                ^
<console>:10: error: type mismatch;
 found   : C
 required: A
              c f c
                  ^

经典参考:

http://www.artima.com/pins1ed/modular-programming-using-objects.html#i-108236764-1

Usually such types are too specific to be useful, which is why the compiler is reluctant to insert them automatically.

这是否符合您的要求?我不知道这是不是最好的方法,但是...

trait Foo {
  class Tag[U <: AnyRef](x: U) {
    type UT = U
  }
  val tag = new Tag(this)
  def injectFrom[T <: tag.UT](same: T): Unit = {
    println("Injected")
    ()
  }
}

class NotBar
{
}

class Bar extends Foo
{
  injectFrom(this)
  val notThis = new NotBar
  injectFrom(notThis)
}

结果:

x.scala:20: error: inferred type arguments [NotBar] do not conform to method injectFrom's type parameter bounds [T <: Bar.this.tag.UT]
  injectFrom(notThis)
  ^
x.scala:20: error: type mismatch;
 found   : NotBar
 required: T
  injectFrom(notThis)
             ^
two errors found

编辑:这可能不是您要查找的内容,因为这将允许注入此 class 的任何子类型,而不是从实例的特定子类型注入?

this.type 是单个对象或 class 实例的类型,而不是 class.

在下面的代码中a1a2会有不同的this.type:

class A
val a1 = new A()
val a2 = new A()

所以类型参数 [T <: this.type] 没有意义,因为你不能 extend 一个 object 或一个实例。

如果你想有一个设计,让 subclasses 中的函数只能接受那个 subclass 的实例(因此 subclasses 的实例只能那个 subclass) 你必须通过使用类型参数或类型变量来明确类型:

trait A {
    type Self
    def injectFrom(same: Self): Unit = ???
}
class B extends A {
    type Self = B
}
class C extends A {
    type Self = C
}

或者 Scala 在 Ordered 实现中所做的事情:

trait A[Self] {
    def injectFrom(same: Self): Unit = ???
}
class B extends A[B]
class C extends A[C]

在这两种情况下,new B().injectFrom(new B()) 会编译但 new B().injectFrom(new C()) 不会。

修改第二个例子,让你在A里面有this injectFrom this:

trait A[Self] {
    def injectFrom(same: A[Self]): Unit = ???
    def test() {
        this injectFrom this
    }
}