Scala中的Trait vs Abstract类,带有构造函数参数

时间:2016-01-17 08:45:36

标签: scala

定义动物的这些方法有何不同之处:

第一种方式:

trait Animal {
  def color: String
}

第二种方式:

trait Animal {
  val color: String
}

第三种方式:

abstract class Animal(color: String) {}

Dog是Animal的一个子类。考虑定义Animal的第一种方式和第二种方式,以下定义Dog的方法有哪些区别:

第一种方式:

case class Dog() extends Animal {
  override def color:String = "black"
}

第二种方式:

case class Dog() extends Animal {
  val color = "black"
}

第三种方式:

case class Dog(color: String) extends Animal {}

第四种方式:

case class Dog(override val color: String) extends Animal(color) {}

1 个答案:

答案 0 :(得分:3)

哇,这里有很多答案。

关于你的第一个问题,如果你使用val,那么所有子类也必须使用val。如果使用def,子类可以使用def,val或lazy val来实现它。如果color是一个稳定的,不可变的值,那么在特征中将其声明为“val”是有道理的,因为它强制要求具体子类中的所有实现也是不可变的。

第三种方法使颜色仅在构造函数体中可用,而不是从外部可见。但是,如果你写了

abstract class Animal(val color: String) {}

然后它将与第二种方式相同,只使用抽象类而不是特征。您可以创建一个新动物并访问其颜色属性。

关于dog,将颜色定义为def意味着每次调用它时都会计算它(即当有人试图访问myDog.color时)。将其定义为val意味着它将是在创建dog对象时一次性计算的不可变值。如果它是一个懒惰的val,那么它将一劳永逸地计算,但不是在创建狗时,而是在调用其颜色属性时(计算被推迟到使用点,因此“懒惰”)。

如上所述,如果动物特性使用val,那么Dog也必须使用val。如果Animal使用def,那么Dog可以将其实现为def,val或lazy val。

编写Dog的第三种方法只是在编写带有类参数的Animal时提供参数(在动物案例中也是第三种方式)。正如我之前所说,在这种情况下,您无法从外部访问颜色属性(即,使用val myDog = new Dog(“blue”)并访问myDog.color)。

编写一只狗的第四种方法是实现Animal,以防它以我在上面的代码中显示的方式编写(使用val关键字)。现在颜色属性将可见。覆盖不是强制性的,因为你正在实现一个抽象方法,而不是覆盖一个具体的方法,但你可以保留它,如果你喜欢(这样编译器会警告你,如果你拼错“颜色”或有人从Animal类中删除颜色)。

也许this article也可以提供帮助。