Scala singleton工厂和类常量

时间:2010-12-15 14:46:49

标签: scala syntax scala-2.8

好的,在关于'Class Variables as constants'的问题中,我得到的事实是,在“官方”构造函数运行之后(即直到你有一个实例),常量才可用。但是,如果我需要伴侣单身人士在课堂上进行调用该怎么办:

object thing {
    val someConst = 42
    def apply(x: Int) = new thing(x)
}

class thing(x: Int) {
    import thing.someConst
    val field = x * someConst
    override def toString = "val: " + field
}

如果我先创建伴随对象,那么'new thing(x)'(在伴侣中)会导致错误。但是,如果我首先定义类,'x * someConst'(在类定义中)会导致错误。

我也尝试将类定义放在单例中。

object thing {
    var someConst = 42

    def apply(x: Int) = new thing(x)

    class thing(x: Int) {
        val field = x * someConst
        override def toString = "val: " + field
    }
}

然而,这样做会给我一个'thing.thing'类型的对象

val t = thing(2)

结果

t: thing.thing = val: 84

我提出的唯一有用的解决方案是创建一个抽象类,一个伴侣和一个内部类(扩展抽象类):

abstract class thing

object thing {
    val someConst = 42
    def apply(x: Int) = new privThing(x)

    class privThing(x: Int) extends thing {
        val field = x * someConst
        override def toString = "val: " + field
    }
}

val t1 = thing(2)
val tArr: Array[thing] = Array(t1)

好的,'t1'仍然具有'thing.privThing'类型,但它现在可以被视为'事物'。

然而,它仍然不是一个优雅的解决方案,谁能告诉我更好的方法呢?

PS。我应该提一下,我在Windows 7上使用Scala 2.8.1

2 个答案:

答案 0 :(得分:12)

首先,您看到的错误(您没有告诉我它是什么)不是运行时错误。 thing单例初始化时不调用thing构造函数 - 稍后在调用thing.apply时调用它,因此在运行时没有循环引用。

其次,你在编译时有一个循环引用,但是当你编译一个已保存在磁盘上的scala文件时,这不会导致问题 - 编译器甚至可以解析不同文件之间的循环引用。 (我测试过。我把你原来的代码放在一个文件中并编译它,它运行正常。)

您真正的问题来自于尝试在Scala REPL中运行此代码。 以下是REPL的作用以及为什么这是REPL中的一个问题。您正在输入object thing,一旦完成,REPL会尝试编译它,因为它已到达终点一段连贯的代码。 (分号推断能够在对象的末尾推断出分号,这意味着编译器可以开始处理那段代码。)但是因为你没有定义class thing它无法编译它。当您颠倒class thingobject thing的定义时,您会遇到同样的问题。

解决方案是将class thingobject thing嵌套在某个外部对象中。这将推迟编译,直到外部对象完成,此时编译器将同时看到class thingobject thing的定义。您可以在此之后立即运行import thingwrapper._,以使class thingobject thing在REPL的全局范围内可用。 当您准备将代码集成到某个文件中时,只需抛弃外部类thingwrapper

object thingwrapper{
   //you only need a wrapper object in the REPL
   object thing {
       val someConst = 42
       def apply(x: Int) = new thing(x)
   }   

   class thing(x: Int) {
       import thing.someConst
       val field = x * someConst
       override def toString = "val: " + field
   }   
}

答案 1 :(得分:0)

Scala 2.12或更高版本可能会受益于sip 23,它只是(2016年8月)传递到下一次迭代(被认为是“好主意”,但是是一个在制品)

  

基于文字的单身人士类型

     

Singleton类型弥合了价值水平和类型水平之间的差距,因此允许在Scala中探索通常仅在支持全谱依赖类型的语言中可用的技术。

     

Scala的类型系统可以模拟常量(例如42"foo"classOf[String])。
  这些是在object O { final val x = 42 }之类的情况下推断出来的。它们用于表示和传播编译时常量(请参阅6.24 Constant Expressions中的4.1 Value Declarations and Definitions和“常量值定义”的讨论。)   但是,没有表达这种类型的表面语法。这使得需要它们的人可以创建可以提供解决方法的宏(例如 shapeless )。
  这可以通过相对简单的方式进行更改,因为启用此功能的整个机制已经存在于scala编译器中。

type _42 = 42.type
type Unt = ().type
type _1 = 1 // .type is optional for literals
final val x = 1
type one = x.type // … but mandatory for identifiers