Scala继承和特质

继承

  • 继承是类的扩展
    • extends 是Scala中实现继承的保留字
    • 子类能够重写超类的成员(具有相同的名称和参数)
    • 单例对象同样能从类中继承,和类的继承语法相同
  • 重写
    • Scala中使用override保留字进行方法,字段的重写

    • override保留字实际使用类似与private,因此,其也可以写在类定义的参数中

      class A(override val a:String) extends B {...}
      
    • 子类的重写或修改Scala会检查其超类,但是,超类的修改并不会检查其子类

    • 重写包括字段和方法,但参数不同的方法可以不重写

      class A { def fun(m:String)={...}}
      
      class B extends A{ def fun ={...}}
      
      
      重写规则:
      • 重写 def

        用val :利用val能重写超类用没有参数的方法(getter)
        用def:子类的方法与超类方法重名
        用var:同时重写getter、setter方法,只重写getter方法报错

      • 重写val

        用val:子类的一个私有字段与超类的字段重名,getter方法重写超类的getter方法

      • 重写var

        用var:且当超类的var是抽象的才能被重写,否则超类的var都会被继承

        abstract class A {
          val a = 25   // 可在子类中用val重写
          var b = 15   // 不可在子类中用var重写,因为不是抽象的
          var c: Int   // 可在子类中用var重写
          def fun1     // 可在子类中用val重写
          def fun      // fun的getter方法,可在子类中用var重写
          def fun_     // fun的setter方法,用var重写setter后,还要用var重写此方法
          def fun3(m:Char)  // 可在子类中用def重写
        }
      
    • 子类中,def只能重写超类的def,val能重写超类的val或不带参数的def,var只能重写超类中抽象的var或者超类的getter/setter

  • 抽象

    • 不能被实例的类叫做抽象类
    • 抽象类的某个或某几个成员没有被完整定义,这些没有被完整定义的成员称为抽象方法或抽象字段
    • 用abstract保留字标记抽象类
    • 只要类中有任意一个抽象成员,必须使用abstract标记
    • 重写抽象方法、抽象字段不需要使用override保留字
    abstract class A {
    
    val name:Array[String]    //抽象的val,带有一个抽象的getter方法
    
    var num:Int       //抽象的var,带有抽象的getter/setter方法
    
    def sign  //没有方法体/函数体,是一个抽象方法
    
    }
    
    

  • 保护

    • 当一个类不希望被继承、拓展时,可在类声明前加上final保留字

      final class A {...}
      
    • 当一个类的某些成员不希望被重写时,可以在成员声明前加上final保留字

      class A { final def sign{...} }
      
    • 当超类中的某些成员需要被子类继承,又不想对子类以外成员可见时,在成员声明前加上protected保留字

       class A { protected def sign{...} }
      
    • protected[this],将访问权限定于当前对象,类似于private[this]

    • 类中protected的成员对其子类可见,对其超类不可见

  • 构造

    • 子类构造器的运行在超类构造器运行之后

    • 在超类的构造器中调用val声明的成员被子类重写后,返回值可能不正确:

      class A {
        val a = 10
        val arr = new Array[Int](a)
      }
      
      class B extends A {
        override val a = 3 
      }
      
      val obj = new A
      
      

      构造B对象前先执行A的构造器,变量a被初始化为10,类A为了初始化arr数组,需要调用变量a,但a被子类B进行了重写,但此时B的构造器还没被调用,所以变量a的值未被初始化,因而返回0,则arr就被初始化成长度为0的数组,A的构造器执行完毕,再执行B的构造器,此时变量a被初始化为7

      解决办法:

      1. 将val声明为final
      2. 在超类中将val声明为lazy
      3. 在子类中使用提前定义语法
      
      提前定义

      所谓的“提前定义”语法让你可以在超类的构造器执行之前初始化子类的val字段。需要将val字段放到extends关键字之后的一个块中,并后接with保留字

      class B extends {
        overried val a = 7
      } with A {...}
      

      注意:提前定义的等号右侧只能引用之前已有的提前定义,而不能使用类中其他的定义或方法。

特质

  • 在scala中,一个类只能继承一个父类,但可以扩展多个特质
  • 多重继承
    • Scala不支持多重继承,取而代之的是特质

    • 一个子类只能拥有一个超类,一个超类能拥有多个子类

    • Scala使用特质达到类似多重继承的效果

    • 一个类可以扩展自一个或多个特质,一个特质可以被多个类扩展

    • 特质能限制被什么样的类所扩展

      为什么不支持多重继承?
      • 若一个子类继承自不同的超类,不同的超类中同名成员子类不知如何处理
      • 多重继承产生菱形继承问题
      • 解决多重继承可能导致的问题消耗的资源远比多重继承产生的价值高
  • 使用特质
    • 特质是Scala里代码复用的基础单元,封装了方法和字段的定义

    • 特质的定义使用保留字trait,具体语法与类定义相似,除了不能拥有构造参数

      trait A { def fun(m:Int,n:Int)=if(m>=n) 1 }
      
    • 一旦特质被定义了,就可以混入到类中

      class B extends A {...}
      
    • 当要混入多个特质时,利用with保留字

      class D extends A with B with C {...}
      
    • 特质的成员可以是抽象的,而且,不需要使用abstract声明

    • 重写特质的抽象方法无需给出override

    • 多个特质重写同一个特质的抽象方法需给出override

    • 除了在类定义中混入特质以外,还可以在特质定义中混入特质,以及构造对象时混入特质

    // 定义特质时混入 
    trait B extends A {...}
    
    // 构造对象时混入
     val obj = new C with B
    
  • 特质构造
    • 特质的构造是有顺序的,从左到右
    • 构造器的构造顺序:
      • 超类---》父特质----》第一个特质---》第二个特质(父特质不重复构造)--- 》... ---》类
    class A extends B with C with D
    

    串接B,C,D特质并去掉重复项,右侧胜出

  • 特质应用

    特质的一个主要应用方面在于接口,根据类已有的方法自动为类添加方法

    • 利用特质实现富接口:
      • 构造一个具有少量抽象方法和大量基于抽象方法的具体方法的特质
      • 只要把特质混入类中,通过类重写抽象方法后,类便自动获得大量具体方法

    特质的另一个应用方面在于:为类提供可堆叠的改变(super保留字)

    • 当为类添加多个互相调用的特质时,从最后一个开始进行处理
    • 在类中super.fun() 这样的方法调用是静态绑定的,明确是调用它的父类的 fun() 方法
    • 在特质中写下了 super.foo() 时,它的调用是动态绑定的。调用的实现将在每一次特质被混入到具体类的时候才被决定
    • 特质混入的次序的不同其执行效果也就不同
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339