无界通配符类型参数与抽象类型

时间:2015-03-30 13:07:12

标签: scala

是否有人能够阐明这两个例子之间的真正差异。

object ExampleA {
    trait Bar { def n: Int }
    trait Foo[B <: Bar] { def bar: B }

    def getBarIntFromFoo(foo: Foo[_]) =
        getBarInt(foo.bar)

    def getBarInt(bar: Bar) =
        bar.n
}

object ExampleB {
    trait Bar { def n: Int }
    trait Foo {
        type B <: Bar
        def bar: B
    }

    def getBarIntFromFoo(foo: Foo) =
        getBarInt(foo.bar)

    def getBarInt(bar: Bar) =
        bar.n
}

我认为唯一的区别在于你引用它们的方式,但实际上只有ExampleB编译而ExampleA会导致:

[error] type mismatch;
[error] found   : _$1
[error] required: ExampleA.Bar
[error]      getBarInt(_)
[error]                ^

两个Foo都有相同的类型约束(B <: Bar),所以我有点迷失。我想我只是误解了一些非常基本的东西。

2 个答案:

答案 0 :(得分:3)

_中的Foo[_]是一种存在类型。如果Any的上限为Foo[B],则不会在其上设置任何类型边界,它将被假定为Bar。这意味着foo.bar被假定为Any,而不是Bar。因此,getBarInt(foo.bar)失败,因为编译器认为您正在传递Any而不是Bar

如果您希望参数为Foo[_],则必须在此处限定。

object ExampleA {
    trait Bar { def n: Int }
    trait Foo[B <: Bar] { def bar: B }

    def getBarIntFromFoo(foo: Foo[_ <: Bar]) =
        getBarInt(foo.bar)

    def getBarInt(bar: Bar) =
        bar.n
}

来自SLS

  

Scala支持存在类型的占位符语法。通配符类型的格式为_ >: L <: U。两个绑定条款都可以省略。如果缺少下限项>: L,则假定为>: scala.Nothing如果缺少上限条款<: U,则假定<: scala.Any 。通配符类型是存在量化类型变量的简写,其中存在量化是隐含的。

带有别名类型的第二个示例并不会受此影响。

答案 1 :(得分:3)

m-z在这里是正确的。但您可能会问的一个问题是,为什么Scala要求您在<: Bar的定义已经要求时重复绑定Foo

原因是为了与

保持一致
def blah[A](f: Foo[A])

这里[A]将由调用者提供,因此不允许Foo对其应用任何额外的约束(否则很难确定blah的确切定义)。这意味着,通常Foo[x]无法对X应用约束,即使在某些情况下它也是安全的。

但是,未来有计划将存在类型重新实现为类型变量,所以这种行为可能会在某一天发生变化。