问题 Scala中方法类型参数化的结构类型?


考虑以下Scala代码(例如,在REPL中)

object A{def foo:Unit = {}}
object B{def foo:Unit = {}}

def bar[T <: Any {def foo: Unit}](param: T*):Unit = param.foreach(x => x.foo)

bar(A, A)  // works fine
bar(B, B)  // works fine
bar(A, B)  // gives error

前两个工作正常。第三个给出错误:

error: inferred type arguments [ScalaObject] do not conform to method bar's type parameter bounds [T <: Any{def foo: Unit}]

有什么方法可以做我想要的吗?


1758
2017-09-29 20:06


起源



答案:


这通常称为结构类型,而不是鸭子类型。我编辑了你的头衔。 :)

我认为你的问题是由定义类型参数引起的 T 然后以不变的方式使用它。 T 只能引用一种具体类型,但您有不同类型的参数 A 和 B

这有效:

 def bar(param: {def foo: Unit}*) = param.foreach(x => x.foo)

编辑:使用类型 别号 也有效:

 type T = {def foo: Unit}
 def bar(param: T*) = param.foreach(x => x.foo)

这是有效的,因为编译器将简单地替换结构类型来代替其别名, T。替换后,此示例与上面的示例完全相同。


14
2017-09-29 20:14



这是使Scala如此出色的另一个功能之一。 - Jus12
但是:很多人会说,要小心!结构打字使用反射...... - Ben James
它是否在运行时使用反射?我认为它不应该使用反射,因为我的代码中的错误发生在编译期间。 - Jus12
它确实在运行时使用反射。请参阅Randall Schulz在此主题中的解释: scala-lang.org/node/6834 - Ben James
我相信如果信息在编译时可用,则应该可以插入此信息以便在运行时使用(除非我遗漏了某些内容)。是否有一个可以展示的例子来证明反射是唯一的方法。 - Jus12


答案:


这通常称为结构类型,而不是鸭子类型。我编辑了你的头衔。 :)

我认为你的问题是由定义类型参数引起的 T 然后以不变的方式使用它。 T 只能引用一种具体类型,但您有不同类型的参数 A 和 B

这有效:

 def bar(param: {def foo: Unit}*) = param.foreach(x => x.foo)

编辑:使用类型 别号 也有效:

 type T = {def foo: Unit}
 def bar(param: T*) = param.foreach(x => x.foo)

这是有效的,因为编译器将简单地替换结构类型来代替其别名, T。替换后,此示例与上面的示例完全相同。


14
2017-09-29 20:14



这是使Scala如此出色的另一个功能之一。 - Jus12
但是:很多人会说,要小心!结构打字使用反射...... - Ben James
它是否在运行时使用反射?我认为它不应该使用反射,因为我的代码中的错误发生在编译期间。 - Jus12
它确实在运行时使用反射。请参阅Randall Schulz在此主题中的解释: scala-lang.org/node/6834 - Ben James
我相信如果信息在编译时可用,则应该可以插入此信息以便在运行时使用(除非我遗漏了某些内容)。是否有一个可以展示的例子来证明反射是唯一的方法。 - Jus12