18

我在“不耐烦的 Scala”的练习 5.7 中,我需要创建一个类 Person,它在构造函数上采用name:String并具有 2 个属性firstNamelastName填充的名称由空格分隔。我的第一次试验是:

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 
}

但我仍然想得到我的问题的答案。

4

5 回答 5

14

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

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

编辑:

您想要做的事情可以通过伴随的工厂方法和默认构造函数来实现,该构造函数将 first 和 last 作为参数:

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 无法在构造函数中创建临时变量。

于 2012-04-15T13:56:25.983 回答
4

我的想法很简单

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

如果您想排除通用代码,那么 drexin 对数组的回答非常好。但它需要读者在第 5 章中没有的知识。但是,带元组的 var 已在第 4 章中介绍,因此以下内容触手可及:

class Person(n: String) {
  val (firstName, lastName) = { val ns = n.split(" "); (ns(0), ns(1)) }
}
于 2012-06-12T15:53:51.123 回答
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 " "
  }
}
于 2012-04-15T14:52:31.050 回答
2

一种避免定义提取器或需要伴随对象方法的方法是

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

包含在其中的 scala 表达式{..}具有其中最后一个子表达式的值

于 2015-10-12T12:16:26.307 回答
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(与 name 相同)没有 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);
}
于 2016-11-24T12:31:32.847 回答