定义动物的这些方法有何不同之处:
第一种方式:
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) {}
答案 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也可以提供帮助。