Scala中的构造函数局部变量

时间:2012-04-15 13:40:12

标签: scala

我正在练习5.7“Scala for the Impatient”,我需要在构造函数中创建一个带有名称:String 的类Person,并且有2个属性 firstName lastName 。我的第一次试验是:

class Person(name:String) {
  private val nameParts = name.split(" ")

  val firstName = nameParts(0)
  val lastName = nameParts(1)
}

问题是,现在 nameParts 仍然是在类中始终可见的私有字段,实际上应该只存在于构造函数的本地环境中。我想要的Java等价物是:

 class Person{
    private final String firstName;
    private final String lastName;

    Person(String name){
        final String[] nameParts = name.split(" ");
        firstName = nameParts[0];
        lastName = nameParts[1];
    }
 }

这里, nameParts 只存在于构造函数中,这正是我的目标。有关如何在Scala中完成此操作的任何提示?

注意:我最终找到了更“Scalesque”的方式:

class Person(name:String) {
    val firstName::lastName::_ = name.split(" ").toList 
}

但我仍想回答我的问题。

5 个答案:

答案 0 :(得分:13)

有一种方法可以避免private val。只需使用Array的提取器:

class Person(name: String) {
  val Array(first, last) = name.split(" ")
}

编辑:

你想要做什么可以通过同伴的工厂方法和第一个和最后一个作为参数的默认构造函数来实现:

class Person(val first: String, val last: String)

object Person {
  def apply(name: String) = {
    val splitted = name.split(" ")
    new Person(splitted(0), splitted(1))
  }
}

scala> Person("Foo Bar")
res6: Person = Person@37e79b10

scala> res6.first 
res7: String = Foo

scala> res6.last
res8: String = Bar

但对于这个简单的案例,我更喜欢我的第一个建议。

链接中的示例也可以使用,但它与我的第一个示例相同。 Afaik无法在构造函数中创建临时变量。

答案 1 :(得分:5)

我想到的只是

class Person(n: String) {
  val firstName = n.split(" ")(0)
  val lastName = n.split(" ")(1)
}

如果你想分解公共代码,那么drexin对数组的回答非常好。但它需要读者在第5章中没有的知识。但是,第4章介绍了使用元组的var,因此以下内容是可以实现的:

class Person(n: String) {
  val (firstName, lastName) = { val ns = n.split(" "); (ns(0), ns(1)) }
}

答案 2 :(得分:3)

只是@drexin回答的补充。在您的示例class Person(name:String)中,构造函数参数仍存储为private[this] val name: String,可以在类中访问,例如:

class Person(name:String) {
  def tellMeYourName = name
}

如果您真的想避免这种情况,可以创建一个伴随对象并将主要构造函数设为私有:

class Person private (val fName: String, val lName: String)

object Person {
  def apply(name: String) = {
    val Array(fName, lName) = name split " "
    new Person(fName, lName)
  }
}

另一种方法是使用伴侣对象创建特征Person

trait Person {
  val lName: String
  val fName: String
}
object Person {
  def apply(name: String) = new Person {
    val Array(lName, fName) = name split " "
  }
}

答案 3 :(得分:1)

避免定义Extractors的方法或对伴随对象方法的需求是

class Person(name: String) {    
  val (firstName, lastName) = {
    val nameParts = name.split(" ")
    (nameParts(0), nameParts(1))
  } 
}
object Example {
  println(new Person("John Doe").firstName)
}

{..}中包含的scala表达式具有

中最后一个子表达式的值

答案 4 :(得分:1)

执行此操作的常见scala方法是使用伴随对象(如前面的答案中所述)。

但是,有些情况下这可能是一个问题(例如,如果我们继承该类并希望在不知道内部逻辑的情况下调用相关的构造函数,或者我们想通过反射实例化该类)。

我知道如果直接在课堂上这样做的唯一方法是有点费解:

class Person(name: String, x: Seq[String]) {
  val firstName = x(0)
  val lastName = x(1)

  def this(name: String) {
    this(name, name.split(" "))
  }
}

这个想法是x(与名称相同)没有var或val,所以转换为private [this] val。

优化器知道,因为它们不在构造函数之外使用,所以它可以将它们用作普通函数参数,并且不会保存它们。 内部呼叫是使这成为可能的原因。

javap -private Person.class的结果:

public class com.rsa.nw_spark.Person {
  private final java.lang.String firstName;
  private final java.lang.String lastName;
  public java.lang.String firstName();
  public java.lang.String lastName();
  public com.rsa.nw_spark.Person(java.lang.String, scala.collection.Seq<java.lang.String>);
  public com.rsa.nw_spark.Person(java.lang.String);
}

解决方案多次提到如下:

class Person(name: String) {    
  val (firstName, lastName) = {
    val nameParts = name.split(" ")
    (nameParts(0), nameParts(1))
  } 
}

实际上并不起作用。它创建一个保存的临时元组。 javap -private Person.class的结果:

public class com.rsa.nw_spark.Person {
  private final scala.Tuple2 x$1;
  private final java.lang.String firstName;
  private final java.lang.String lastName;
  public java.lang.String firstName();
  public java.lang.String lastName();
  public com.rsa.nw_spark.Person(java.lang.String);
}