Scala多态方法和显式引用

时间:2020-02-23 14:41:48  来源:igfitidea点击:

scala中的方法可以使用值和类型进行参数化。
值参数放在一对括号内,而类型参数放在一对括号内。
例如;

polymorphic.scala

object polymorphic {
def main(args: Array[String]) {
  def add(a: Int, b: Int) {
    val c = a + b;
    println(c)
  }

  def addStrings(a: String, b: String) {
    val s = a + b
    println(s)
  }

  add(72, 67);
  addStrings("Hello", "World");
}
}

在此示例中,+加号运算符用于添加数字以及连接字符串。
加法执行两个数字的加法并返回结果。
addString方法使用相同的+运算符,并连接用户指定的两个字符串。
这是Scala提供的默认运算符多态性。
下图显示了以上程序产生的输出。

Scala方法多态

现在,让我们看一个方法中多态的例子。
我们将编写一个通用方法来获取列表,并其中传递列表中的类型,默认值和项目数。

PolymorphicMethod.scala

object PolymorphicMethod {
def getList[T](x:T, y:Int): List[T] = {
  if (y == 0)
    Nil
  else
    x :: getList(x, y - 1)
}
    
  def main(args: Array[String]) {
    println(getList[Int](3,2))
    println(getList[String]("Hi", 3))
  }
}

下图显示了在Scala IDE中执行上述程序时产生的输出。

Scala明确键入引用

有时我们需要在程序中明确指定值" this"的类型。
让我们看一下这种情况的示例。

我们有一个Train抽象类,如下所示。

Train.scala

abstract class Train {
type Link;
type Compartment <: CompartmentIntf
abstract class CompartmentIntf {
  def join(compartment: Compartment): Link
}
def compartments: List[Compartment]
def links: List[Link]
def addCompartment: Compartment
}

抽象类Train由链节和车厢列表组成。
定义了抽象类CompartmentIntf,并包含将Compartment作为参数的join方法。
声明了方法spacer,links和addCompartment,但是将在具体的类中定义实现。

现在,我们将定义抽象类MetroTrain,将火车类扩展为;

MetroTrain.scala

abstract class MetroTrain extends Train {

type Link <: LinkImpl
class LinkImpl(comptA: Compartment, comptB: Compartment) {
  def c1 = comptA
  def c2 = comptB
}

class CompartmentImpl extends CompartmentIntf {
  def join(compartment: Compartment): Link = {
    val link = newLink(this, compartment)
    links = link :: links
    link
  }
}

protected def newCompartment: Compartment
protected def newLink(c1: Compartment, c2: Compartment): Link
var compartments: List[Compartment] = Nil
var links: List[Link] = Nil
def addCompartment: Compartment = {
  val compartment = newCompartment
  compartments = compartment :: compartments
  compartment
}
}

此类提供了扩展Train类的部分实现。
实现细节是开放的,因此链接和隔离专区被抽象化了。
由于创建新的隔离专区和链接对象是必需的,因此我们添加了工厂方法newLink和newCompartment。
方法addCompartment和join是使用工厂方法定义的。

但是,上面的类将在第11行中引发编译错误为"类型不匹配;找到:CompartmentImpl.this.type(具有基本类型MetroTrain1.this.CompartmentImpl):MetroTrain1.this.Compartment",因为" this"被分配了CompartmentImpl类型,因此它与相应工厂方法要求的Compartment类型不兼容。

让我们通过如下对CompartmentImpl使用显式类型的自引用来解决这种类型不匹配的问题。

class CompartmentImpl extends CompartmentIntf {
self: Compartment =>
def join(compartment: Compartment): Link = {
  val link = newLink(this, compartment)
  links = link :: links
  link
}
}

根据" CompartmentImpl"的新定义,其类型为Compartment。
我们指定显式类型的引用,以便" CompartmentImpl"表示要实例化的Compartment的子类型。

现在,我们将一个具体的类ConcreteMetroTrain定义为;

ConcreteMetroTrain.scala

class ConcreteMetroTrain extends MetroTrain {
type Link = LinkImpl
type Compartment = CompartmentImpl
protected def newCompartment: Compartment = new CompartmentImpl
protected def newLink(c1: Compartment, c2: Compartment): Link =
  new LinkImpl(c1, c2)
}

现在我们可以实例化CompartmentImpl,因为现在我们知道CompartmentImpl表示Compartment类型的子类型。
现在创建一个Scala对象TestTrain作为;

TestTrain.scala

object TestTrain {
def main(args: Array[String]) {

  println("Linking Compartments.....")
  val t: Train = new ConcreteMetroTrain
  val c1 = t.addCompartment
  val c2 = t.addCompartment
  val c3 = t.addCompartment
  c1.join(c2)
  c2.join(c3)
}
}