Scala:变量类型推断会影响性能吗?

时间:2014-10-30 10:37:14

标签: scala types type-inference

在Scala中,您可以通过指定类型来声明变量,如下所示:(方法1)

var x : String = "Hello World"

或者您可以让Scala自动检测变量类型(方法2)

var x = "Hello World"

为什么要使用方法1?它有性能优势吗?
一旦声明了变量,在方法1或方法2声明的所有情况下,它的行为是否完全相同?

4 个答案:

答案 0 :(得分:14)

类型推断是在编译时完成的 - 它实际上是编译器搞清楚你的​​意思,填写空白,然后编译生成的代码。

这意味着类型推断不会有运行时成本。但是,编译时间成本有时会令人望而却步,并且需要您明确注释某些表达式。

答案 1 :(得分:4)

使用这两种变体,您不会有任何性能差异。 它们都将被编译为相同的代码。

答案 2 :(得分:3)

其他答案假设编译器推断出您认为推断的内容。

很容易证明,在定义中指定类型将设置定义的RHS的预期类型和指南类型推断。

例如,在构建某个集合的方法中,A被推断为Nothing,这可能不是您想要的:

scala> def build[A, B, C <: Iterable[B]](bs: B*)(implicit cbf: CanBuildFrom[A, B, C]): C = {
     | val b = cbf(); println(b.getClass); b ++= bs; b.result }
build: [A, B, C <: Iterable[B]](bs: B*)(implicit cbf: scala.collection.generic.CanBuildFrom[A,B,C])C

scala> val xs = build(1,2,3)
class scala.collection.immutable.VectorBuilder
xs: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)

scala> val xs: List[Int] = build(1,2,3)
class scala.collection.mutable.ListBuffer
xs: List[Int] = List(1, 2, 3)

scala> val xs: Seq[Int] = build(1,2,3)
class scala.collection.immutable.VectorBuilder
xs: Seq[Int] = Vector(1, 2, 3)

显然,无论是获得List还是Vector,对运行时性能都很重要。

这是一个很蹩脚的例子,但在许多表达式中,除非导致性​​能问题,否则你不会注意到中间集合的类型。

示例对话:

https://groups.google.com/forum/#!msg/scala-language/mQ-bIXbC1zs/wgSD4Up5gYMJ

http://grokbase.com/p/gg/scala-user/137mgpjg98/another-funny-quirk

Why is Seq.newBuilder returning a ListBuffer?

https://groups.google.com/forum/#!topic/scala-user/1SjYq_qFuKk

答案 3 :(得分:1)

在您给出的简单示例中,生成的字节代码没有区别,因此性能没有差异。它在编译速度上也没有明显的区别。

在更复杂的代码(可能涉及implicits)中,您可能遇到通过指定某些类型可以显着改善编译类型性能的情况。但是,我会完全忽略这一点,除非你遇到它 - 指定类型或不用于其他更好的理由。

更符合您的问题,有一个非常重要的案例,最好指定类型以确保良好的运行时性能。请考虑以下代码:

val x = new AnyRef { def sayHi() = println("Howdy!") }
x.sayHi

该代码使用反射来调用sayHi,这是一个巨大的性能影响。由于这个原因,Scala的最新版本会警告您此代码,除非您为其启用了语言功能:

warning: reflective access of structural type member method sayHi should be enabled
by making the implicit value scala.language.reflectiveCalls visible.
This can be achieved by adding the import clause 'import scala.language.reflectiveCalls'
or by setting the compiler option -language:reflectiveCalls.
See the Scala docs for value scala.language.reflectiveCalls for a discussion
why the feature should be explicitly enabled.

然后您可以将代码更改为此代码,但不使用反射:

trait Talkative extends AnyRef { def sayHi(): Unit }
val x = new Talkative { def sayHi() = println("Howdy!") }
x.sayHi

因此,您通常希望在以这种方式定义类时指定变量的类型;这样,如果您无意中添加了需要反射调用的方法,您将收到编译错误 - 不会为变量类型定义方法。因此,虽然指定类型不会使代码运行得更快,但 的情况是,如果代码很慢,指定类型会使编译失败。

val x: AnyRef = new AnyRef { def sayHi() = println("Howdy!") }
x.sayHi  // ERROR: sayHi is not defined on AnyRef

当然,您可能还需要指定类型的其他原因。它们是方法/函数的形式参数以及递归或重载方法的返回类型所必需的。

此外,您应该始终为公共API中的方法指定返回类型(除非它们只是显而易见),或者您最终可能会得到与您预期不同的方法签名,然后冒险破坏API的现有客户端修复签名。

您当然可以故意扩大类型,以便稍后可以将其他类型的事物分配给变量,例如。

var shape: Shape = new Circle(1.0)
shape = new Square(1.0)

但在这些情况下,没有性能影响。

指定类型也可能会导致转换,当然这会对转换带来任何性能影响。