繁体   English   中英

Scala 中的继承

[英]Inheritance in scala

创建子类的实例但键入到其超类中时,子类的成员不可访问,为什么? 输入子类的超类的用例是什么?

class Fruits{
  val fruit=12
}
class Apple extends Fruits{
  val apple = 90
}
class Orange extends Fruits{
  val orange = 9
}

val aa:Fruits = new Apple
//aa.apple ,This is not accessible, but I have created an instance of Apple, and class Apple contains apple
val ab:Apple = new Apple
ab.apple //Ok

对比变量aa静态类型(编译时)与运行时类型

val aa: Fruit = new Apple
          |           |
     static type  runtime type

关键是要理解编译器在运行时知道类型,只验证静态类型指定的约束。 因为您使用类型注释:Fruit in aa: Fruit明确指示编译器,编译器不知道aa在运行时实际上是Apple 因此aa.apple是一个编译器错误,因为Fruit没有字段apple

继承的一个用例是启用子类型多态性

trait Shape {
  def area: Double
}

class Circle(val radius: Float) extends Shape {
  override def area: Double = radius * radius * Math.PI
}

class Rectangle(val width: Double, val height: Double) extends Shape {
  override def area: Double = width * height
}

object ShapeArea {
  def area(shape: Shape): Double = shape.area // <=== define method once for all kinds of shape
}

import ShapeArea._
area(new Circle(2))       // res0: Double = 12.566370614359172
area(new Rectangle(2, 3)) // res1: Double = 6.0

作为旁注,在惯用的函数式编程 Scala 中,存在一种替代的临时多态(类型类)方法:

sealed trait Shape
case class Circle(radius: Float) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape

trait Area[T <: Shape] {
  def area(shape: T): Double
}

object Area {
  def area[T <: Shape](shape: T)(implicit ev: Area[T]): Double = ev.area(shape) // <== define method once for all kinds of shape

  implicit val circleArea: Area[Circle] =
    (circle) => circle.radius * circle.radius * Math.PI

  implicit val rectangleArea: Area[Rectangle] =
    (rectangle) => rectangle.height * rectangle.width
}

import Area._
area(Circle(2))       // res0: Double = 12.566370614359172
area(Rectangle(2, 3)) // res1: Double = 6.0

这有助于明确限制可访问/公开的有关引用对象的信息量; 它可以被视为一种信息隐藏形式。 避免意外依赖有关对象的无关信息可能很有用。

正如其他答案已经指出的那样,原因是编译器不知道aa是一个苹果。 它认为aa是水果,因为这里提供了静态类型Fruits val aa:Fruits = new Apple在您的代码中。

我们拥有的一个真实用例是 BusinessLayer 和 DataAccessLayer 之间的交互。 BusinessLayer 必须在持久存储中保存一些对象,它会调用 DataAccessLayer 的 save() 方法来实现这一点。 BusinessLayer 不关心 save() 的实现。 它取决于 DataAccessLayer 如何定义 save() 方法。
在生产中, save() 方法将保存对象,例如,在 postgres 数据库中。 但是当我进行一些本地测试时,我可能不想设置 postgres 数据库。 在这种情况下,我想在本地文件系统中将对象另存为 json。 我们怎样才能做到这一点?
我们可以提供 DataAccessLayer 的两种实现: PostgresDataAccessLayerJsonDataAccessLayer 我的本地测试代码将调用 JsonDataAccessLayer 的save()方法,而生产代码将调用 PostgresDataAccessLayer 的save()方法。 代码如下所示:

trait DataAccessLayer {
  def save(obj: SomeType)
}

class PostgresDataAccessLayer extends DataAccessLayer {

  override def save(obj: SomeType) = {
    //logic to save in postgres DB
  }

}

class JsonDataAccessLayer extends DataAccessLayer {

  override def save(obj: SomeType) = {
    //logic to save as json in local file system
  }

}

class BusinessLayer(val dal: DataAccessLayer) {

  def save(obj: SomeType) ={
    dal.save(obj)
  }

}

//Production code
val bsLayerProd = new BusinessLayer(new PostgresDataAccessLayer())

//Test Code
val bsLayerTest = new BusinessLayer(new JsonDataAccessLayer())

您可以在实例化时将 DataAccessLayer 的任何实现的对象传递给 BusinessLayer。 在运行时,将调用底层子类的save()方法。
这种交互,如这里的 BusinessLayer 和 DataAccessLayer 之间,是已知的依赖倒置,即High-level modules should not depend on low-level modules. Both should depend on abstractions High-level modules should not depend on low-level modules. Both should depend on abstractions

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM