0

这是在 scala.2.8.1

我有一个对象配置。其中有一个惰性值,database.

样品 A:

import Config.database

 trait Dao {  
   protected val database = database  
}

样品 B:

import Config

trait Dao {
  protected val database = Config.database
}

在config中,相关代码为:

lazy val database = 
   somethingFromAFile match {
      case "a" => databaseA
      case "b" => databaseB
      case "c" => databaseC
   }

lazy val databaseA = makeDB("a")
lazy val databaseB = makeDB("b")
lazy val databaseC = makeDB("c")

var changes = throw new Exception ("Not yet initialised")
private def makeDB(db: String) = {
    db match {
      case "a" => var changes = x => 2*x; ... //database making stuff
      case "a" => var changes = x => 3*x; ...
      case "a" => var changes = x => 4*x; ...
    }
}

样本 A 和样本 B 中评估数据库的顺序不同。
我认为这不应该是正确的行为。当然这不是直观的。如果这不是错误,有人可以解释为什么选择这种行为吗?

具体行为是在惰性 val 内部,将 var 设置为某个值。在示例 A 中,var 的设置晚于示例 B 中的设置。

编辑所以我才意识到我得到了val database = database. 因此,更改导入会导致变量隐藏,尽管我希望自引用 val 应该发出编译器警告,或者溢出堆栈?

4

2 回答 2

4

我编译了您的两个示例,然后使用javap -c. (在我的示例Config.database中是一个惰性字段,其值为""; 的类型database应该没有影响)。

第一个示例创建以下字节码:

Compiled from "foo.scala"
public class foo extends java.lang.Object implements scala.ScalaObject{
public java.lang.String db();
  Code:
   0:   aload_0
   1:   getfield        #11; //Field db:Ljava/lang/String;
   4:   areturn

public foo();
  Code:
   0:   aload_0
   1:   invokespecial   #17; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   getstatic       #23; //Field Config$.MODULE$:LConfig$;
   8:   invokevirtual   #26; //Method Config$.database:()Ljava/lang/String;
   11:  putfield        #11; //Field db:Ljava/lang/String;
   14:  return

}

第二个示例创建以下相同的字节码:

Compiled from "foo.scala"
public class foo extends java.lang.Object implements scala.ScalaObject{
public java.lang.String db();
  Code:
   0:   aload_0
   1:   getfield        #11; //Field db:Ljava/lang/String;
   4:   areturn

public foo();
  Code:
   0:   aload_0
   1:   invokespecial   #17; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   getstatic       #23; //Field Config$.MODULE$:LConfig$;
   8:   invokevirtual   #26; //Method Config$.database:()Ljava/lang/String;
   11:  putfield        #11; //Field db:Ljava/lang/String;
   14:  return

}

我什至看不出两者之间的表面差异。


这与导入的实际情况一致 - 它们只是一种防止您必须在任何地方完全限定引用的方法。 import Config._什么都不;它只是将值Config带入顶级范围,以便在编译期间解析器将认为它们有效而没有前缀。

换句话说,

import A.b
...
b

完全一样_

...
A.b

导入只会节省您的输入,它们不会改变代码的含义。如果您看到差异,那么我相信这只有在您的一个导入隐藏另一个定义时才有可能,因此根据导入的措辞方式,对无前缀声明的解释会有所不同。

于 2012-07-16T10:55:54.457 回答
1

Adef可能会导致堆栈溢出,但不会导致val. Aval由getter和setter组成,所以赋值是通过setter完成的,读取是通过getter完成的,不存在循环。存储的值将与 JVM 初始化的值相同:null

自引用变量没有警告,因为有有效的用例。例如,请参阅人们非常喜欢的常见“素数”或“斐波那契”流示例。

于 2012-07-16T13:44:21.720 回答