[scala-spark]6. 继承与特质trait

1. 继承

  • 定义抽象类
abstract class Element{
  def contents:Array[String]
}

抽象类的方法没有实现,抽象类的类本身必须被abstract修饰。而方法只要没有实现,它就是抽象的,不需要加abstract

  • 定义无参方法
abstract class Element{
  def contents:Array[String]
  def height:Int = contents.lenght
  def width:Int = if(height==0) 0 else contents(0).length
}

无参数方法在Scala里十分常见。相对的如果带有空括号的方法,比如def height():Int ,被称为空括号方法。推荐使用的方式是:只要方法中没有参数且方法仅能够通过读取所包含对象的属性去访问可变状态(特指方法不能改变可变状态),就使用无参数方法。这个惯例支持统一访问原则。简单来说,就是说客户代码不应该由属性是通过字段实现还是方法实现而受到影响。例如我们可以把上述代码中的def 换成 val,这种转换会使运行更快。在Scala中调用空括号方法,省略括号是合法的,在无参数方法和空括号方法之间有着极大的自由程度。

abstract class Element{
  def contents:Array[String]
  val height:Int = contents.lenght
  val width:Int = if(height==0) 0 else contents(0).length
}

总之,Scala鼓励定义将不带参数且没有副作用的方法定义成为无参数方法的风格。

  • 定义子类
class ArrayElement(conts:Array[String]) extends Elements{
  def contents:Array[String] = conts
}

ArrayElement类用extends 继承Elements,ArrayElement获得了Elements的所有非私有属性成员,ArrayElement是Elements的子类,Elements是ArrayElement的超类。如果一个类没有extends,编译器会将该类隐式地继承scala.AnyRef,如同Java的Object类一样。 
Scala中,子类可以重写父类属性,可以实现父类抽象属性,继承父类非私有属性。这几点都和Java相同。

  • 重写方法和属性

Scala中的字段和方法属于相同的命名空间,这让字段可以重写无参数方法。比如下面:

class ArrayElement(conts:Array[String]) extends Elements{
  //实现父类的无参数方法(def contents:Array[String])
  val contents:Array[String] = conts
}
  • 调用超类构造器和override关键词
class LineElement(s:String) extends ArrayElement(Array(s)){
  override def width = s.length
  override def height = 1
}

由于LineElement扩展了ArrayElement。并且ArrayElement的构造器带了一个参数Array[String],因此LineElement需要传给超类的主构造器一个参数。调用父类构造器,只要简单地把参数放在超类后面的括号里即可。

Scala要求,若子类成员重写了父类的具体成员则必须带有这个修饰符。但若子类实现的是父类的抽象成员时,则该修饰符也可以省略。若子类并未重写或实现其他基类的成员则禁用这个修饰符。

Scala中的多态和Java中的相同。

  • 定义final成员
final def a : Int = {...}
final class b(a:Int){
...
}
  • 定义工厂方法

定义一个工厂方法来生产Element的各个子类实例,隐藏了实现细节。

object Element {

    /**
      * 私有的类,隐藏实现细节
      *
      * @param cons
      */
    private class ArrayElment(cons: Array[String]) extends Elment {
        val contents = cons
    }

    private class UniformElement(ch: Char, override val height: Int, override val width: Int) extends Elment {
        private val line = ch.toString * width

        def contents = new Array[String](3);
    }

    private class LineElment(s: String) extends ArrayElment(Array(s)) {
        override val height: Int = 1
        override val width: Int = s.length
    }

    /**
      * 通过公有的方法,提供私有类的实例
      *
      * @param contents
      * @return
      */
    def elem(contents: Array[String]): Elment = new ArrayElment(contents)

    def elem(chr: Char, width: Int, height: Int): Elment = new UniformElement(chr, width, height)

    def elem(line: String): Elment = new LineElment(line)
}

2. 特质trait

特质是Scala中代码复用的基本单元。特质中封装了方法和字段的定义。特质功能非常强大。类可以混入任意多个特质,其主要的运用方式是:拓宽瘦接口为胖接口和实现可堆叠的改变。

trait A{
  val tyl : String
  def method : Int {
  ...
  }
}

trait C{
  def method : Int {
  ...
  }
}

class D {
  ...
}

class B with A {
   override def toString = "B with A"
}

class B extends D with A with C{
   ...
}

一旦特质被定义,就可以用extendswith关键字,把它混入类中。特质就像带有具体方法的Java接口,它可以有正常的变量、方法,即类可以做到的事情,特质都可以除了没有构造器。还有一点类的super调用是静态绑定的,而特质则是动态绑定的,这也决定了特质可以实现堆叠式改变。

  • 拓宽瘦接口为胖接口

瘦接口和胖接口的对阵体现了面向对象设计常会面临的实现者和接口用户之间的权衡。胖接口有更多方法,对于调用者来说更为方便,客户可以选择一个完全符合他们功能需要的方法。瘦接口有较少的方法,对于实现者来说简单,但是调用者却要为此写更多的代码,由于没有更多可选的方法,他们或许不得不选一个不太完美匹配的或是重写额外代码。 Java中的接口常常是过瘦而非过胖。Java中的CharSequence接口定义了4个方法。如果CharSequence包含全部的String接口,那实现者将为CharSequence定义一堆可能自己不需要定义的方法。 试想一下,一个特质拥有几个抽象方法,剩余的是大量具有具体方法的方法。接口用户只需要混入该特质,简单地实现抽象方法,就可以拥有所有的具体方法。这是Java中不能做到的。

发布了652 篇原创文章 · 获赞 1575 · 访问量 298万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503

分享到微信朋友圈

×

扫一扫,手机浏览