scala类构造函数参数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了scala类构造函数参数相关的知识,希望对你有一定的参考价值。

有什么区别:

class Person(name: String, age: Int) {
  def say = "My name is " + name + ", age " + age
}

class Person(val name: String, val age: Int) { 
  def say = "My name is " + name + ", age " + age
}

我可以将参数声明为vars,并在以后更改它们的值吗?例如,

class Person(var name: String, var age: Int) {

  age = happyBirthday(5)

  def happyBirthday(n: Int) {
    println("happy " + n + " birthday")
    n
  }
}
答案

对于第一部分,答案是范围:

scala> class Person(name: String, age: Int) {
     |   def say = "My name is " + name + ", age " + age
     | }

scala> val x = new Person("Hitman", 40)

scala> x.name
<console>:10: error: value name is not a member of Person
              x.name

如果你使用valvar为参数添加前缀,那么它们将在课外显示,否则,它们将是私有的,如上面的代码所示。

是的,你可以改变var的值,就像通常一样。

另一答案

这个

class Person(val name: String, val age: Int)

使该字段在该类用户外部可用,例如你以后可以做

val p = new Person("Bob", 23)
val n = p.name

如果将args指定为var,则范围与val相同,但字段是可变的。

另一答案

如果您熟悉Java,可以从这个示例中获得想法:

class Person(name: String, age: Int)

类似于

class Person {
  public Person(String name, int age) {
  }
}

class Person(var name: String, var age: Int) // also we can use 'val'

类似于

class Person {
  String name;
  int age;

  public Person(String name, int age) {
     this.name = name;
     this.age = age;
  }
}

直觉是没有var / val,变量只能在构造函数中访问。如果添加了var / val,则该类将具有相同名称的成员变量。

另一答案

这里的答案非常好,但是我正在通过探索字节代码解决这个问题。当您在类上应用javap时,它会打印出传递的类的包,受保护和公共字段以及方法。我创建了一个Person.scala类,并使用以下代码填充它。

class Person(name: String, age: Int) {
  def say = "My name is " + name + ", age " + age
}

class PersonVal(val name: String, val age: Int) {
  def say = "My name is " + name + ", age " + age
}

class PersonVar(var name: String, var age: Int) {

  age = happyBirthday(5)

  def happyBirthday(n: Int) = {
    println("happy " + n + " birthday")
    n
  }
}

scalac Person.scala编译代码后,它会生成三个名为Person.class, PersonVal.calass , PersonVar.cass的文件。通过为每个类文件运行javap,我们可以看到结构如何:

>>javap Person.class
Compiled from "Person.scala"
public class Person {
  public java.lang.String say();
  public Person(java.lang.String, int);
}

在这种情况下,它没有为Person创建任何类变量,因为它既没有val也没有val声明,因此name和age只能在构造函数中使用。

>>javap PersonVal.class
public class PersonVal {
  public java.lang.String name();
  public int age();
  public java.lang.String say();
  public PersonVal(java.lang.String, int);
}

在这种情况下,它有三个成员,两个用于输入构造函数,另一个用于我们在constructore中声明的成员。但是,我们没有输入构造函数的任何setter,因此我们无法更改值。

>>javap PersonVar.class
public class PersonVar {
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  public int happyBirthday(int);
  public PersonVar(java.lang.String, int);
}

它与PersonVal示例相同,但我们可以使用这些variable_$eq方法更改此情况下的值。它不仅仅是variable =的缩短版本

另一答案

您可以使用case class,在这种情况下,Person类将在类之外提供这些变量。 case class Person(name: String, age: Int)。然后,以下代码将按预期工作。 val z = new Person("John", 20); z.name //John

另一答案

作者使用javap探索字节代码的@Reza的答案帮助我最好地阐明了这个概念。举一个这个案例的一个非常具体的例子,请参考我在生产网络应用程序(Play + Scala)中遇到的以下场景:How to inject parameters into a class/trait method in Scala

如果我不使用val前缀来注入参数authorizationHandler,那么编译器会抛出此错误:

class MyController needs to be abstract, since method authorizationHandler in trait AuthorizationCheck of type => controllers.authapi.AuthorizationHandler is not defined
[error] class MyController @Inject() (authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {
[error]       ^
[error] one error found

可悲的是,错误并没有帮助我找出正确的问题,即val的前缀。

class MyController @Inject()(val authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {

   def myAction = AuthenticatedAction { implicit request =>
     ...
   }
} 

以上是关于scala类构造函数参数的主要内容,如果未能解决你的问题,请参考以下文章

Scala-构造函数

scala入门教程:scala中的面向对象定义类,构造函数,继承

Scala 隐式类必须有一个主构造函数,在第一个参数列表中只有一个参数

快学Scala 第六课 (类构造函数)

Scala类型系统——高级类类型(higher-kinded types)

Scala 数据集映射失败并出现异常没有为零实际参数找到适用的构造函数/方法