码迷,mamicode.com
首页 > 其他好文 > 详细

Scala基础06:继承

时间:2015-07-03 11:50:14      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:

Scala继承

继承是类的扩展

extends是Scala中实现继承的保留字:

class week extends month{...}

说明:

week类继承了month类所有非私有成员。

week类是month类的子类,month类是week类的超类。

子类能重写超类的成员(具有相同名称和参数)。

 

和Java一样,若类被声明为final,则该类不可再被继承。若方法或字段被声明为final,则该方法不可再被重写。

注意:Scala中的final与Java中的final有一点不同,Java中final的字段是不可变的。Scala中没有。

 

 

object继承class

例:

class Student(name:String , age:Int ,val major : String) extends Person(name , age) {
  println("this is subclass of Person , major is :" + major)
}

单例对象同样能从类中继承,与类的继承语法相同:

object day extends week{...}

 

 

重写

Scala中使用override保留字进行方法、字段重写

class week extends month{
  override def firstday = {...}
}

override也可以用于类的主构造器参数:

class week(override val lastday:String) extends month{...}

 

子类的重写或修改Scala会检查其超类,但是,超类的修改并不会检查其子类。

例:

class Student(name:String , age:Int ,val major : String) extends Person(name , age) {
  println("this is subclass of Person , major is :" + major)
  override def toSting = "override toSting"
}

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

class month{ def secondday(m:String)={...}}

class week extends month{ def secondday ={...}}

 

规则:

重写 def

用val :利用val能重写超类用没有参数的方法(getter)

用def:子类的方法与超类方法重名

用var:同时重写getter、setter方法,只重写getter方法报错

重写val

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

重写var

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

例:

class month{

  val one = 25 //可在子类中用val重写

  var two = 15 //不可在子类中用var重写,因为不是抽象的

  var three:Int

  def firstday =                               //可在子类中用val重写

  def now =                                    //可在子类中用var重写

  def now_ =                                

  def lastday(m:Char)={}                //可在子类中用def重写

}

总结:

子类中,def只能重写超类的def,val能重写超类的val或不带参数的def,var只能重写超类中抽象的var或者超类的getter/setter对。

 

 

类型检查和转换

使用isInstanceOf方法测试对象的类是否继承于某个类。使用asInstanceOf方法将引用转换为子类的引用。

if(p.isInstanceOf[Employee]){
  val s = p.asInstanceOf[Employee]  // s的类型为Employee
  // ...
}

 

若p指向Employee类及其子类(比如Manager类)的对象。则p.isInstanceOf[Employee] 返回true。

若p为null,则p.isInstanceOf[Employee]将返回false,则p.asInstanceOf[Employee]将返回null。

若p不是一个Employee,则p.asInstanceOf[Employee]将抛出异常。

 

若需要判断 “p指向的就是一个Employee对象,不是其子类的对象。” 则可以使用:

if (p.getClass == classOf[Employee])

classOf方法定义在scala.Predef对象中,因此会被自动引入。

说明:对于类型检查和转换,Scala中更偏向于使用模式匹配。

 

Scala和Java的类型检查和转换

Scala Java
obj.inInstanceOf[C1] obj instanceof C1
obj.asInstanceOf[C1] (C1)obj
classOf[C1] C1.class

 

匿名子类

 

val alien = new Person("Fred"){
  def greeting = "Greetings , Earthling! My name is Fred"
}

 

 

 

 

抽象类

不能被实例的类叫做抽象类。

抽象类的某个或某几个成员没有被完整定义,这些没有被完整定义的成员称为抽象方法或抽象字段。

用abstract保留字标记抽象类。

abstract year{

val name: Array[String]  //抽象的val,带有一个抽象的getter方法

var num: Int  //抽象的var,带有抽象的getter/setter方法

def sign1 //没有方法体,就是一个抽象方法。

def sign2: Int //没有函数体,就是一个抽象函数。

}

只要类中有任意一个抽象成员,必须使用abstract标记。

重写抽象方法、抽象字段不需要使用override保留字。

抽象字段,即没有初始值的字段。

抽象方法,即没有方法体的方法。

保护

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

final class year{...}

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

class year{

  final def sign{...}

}

当超类中的某些成员需要被子类继承,又不想对子类以外成员可见时,在成员声明前加上protected保留字。

protected[this],将访问权限定于当前对象,类似于private[this]。

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

class year{

  protected def sign{...}

}

辨析:private与private[this]的区别。

继承与构造器

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

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

class month{

  val num = 31

  val days = new Array[Int](num)

}

class week extends month{

  override val num = 7

}

val a=new week

构造week对象前先执行month的构造器,num被初始化为31,month为初始化days数组,调用num,但num被子类week重写了,但因为week构造器还没被调用,

此时num的值未被初始化,因而返回0,days被设置为长度为0的数组,month构造器运行

完毕,执行week构造器,num被初始化为7

解决方法:

将超类的val声明为final

将超类的val声明为lazy

在子类中使用提前定义语法

提前定义

在超类的构造器运行之前初始化子类的字段。

把需要提前定义的语句块放在extends与超类之间,并后接with保留字。

class week extends {override val num =7} with month{...}

提前定义中“=”右侧若需调用类中B成员时,除非B成员已在调用前提前定义:

class week extends{

    override val num=7

    override val num2=num+1 //允许,num已被提前定义

    override val num4=num2+num3 //不允许,num3没有在之前提前定义

}with month{...}

特质

Java8中新增一个特性:default method ,可以在interface中实现的方法。

Scala特质类似Java 8的interface。

Scala类只能继承自一个父类,但可以由多个特质拓展而成。

Scala不支持多重继承,取而代之的是特质。

Trait可以作为工具方法混入到相关类中。

多重继承的问题

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

即:class week extends month,year是不合法的。

为什么?

若一个子类继承自不同的超类,不同的超类中同名成员子类不知如何处理。

多重继承产生菱形继承问题。

解决多重继承可能导致的问题消耗的资源远比多重继承产生的价值高

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

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

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

所有Java interface都能当做是特质使用在Scala中。

当接口使用特质

trait Logger{

  def log(msg:String)

}

实现

scala中没有implements,所以使用extends即可。

class ConsoleLogger extends Logger{

  def log(msg:String){ println(msg) }

}

重写特质的方法,不需要override关键字。

特质中重写抽象方法

trait Logger{

  def log(msg:String)

}

重写特质需要使用override。

trait TimestampLogger extends Logger{

  override def log(msg:String){

    super.log(new java.util.Date()+” ”+msg)   

  }

}

特质是Scala里代码复用的基础单元,封装了方法和字段的定义。

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

trait reset{

  def reset(m:Int,n:Int) = if(m >= n) 1

}

一旦特质被定义了,就可以混入(mixin)到类中。

class week extends reset {...}

当要混入多个特质时,利用with关键字:

class week extends reset with B with C {...}

特质的成员可以是抽象的,而且,不需要使用abstract声明。

同样的,重写特质的抽象方法无需给出override。

但是,多个特质重写同一个特质的抽象方法需给出override。

除了在类定义中混入特质以外,还可以在特质定义中混入特质:

trait reseting extends reset{...}

在对象构造时混入特质:

val five = new month with reseting

特质的构造是有顺序的:从左到右被构造。

构造器按如下顺序构造:

超类

父特质

第一个特质

第二个特质(父特质不重复构造)

技术分享

如果class A extends B1 with B2 with B3....

那么,串接B1、B2、B3...等特质,去掉重复项且右侧胜出。

技术分享

特质的应用

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

利用特质实现富接口:

构造一个具有少量抽象方法和大量基于抽象方法的具体方法的特质。

那么,只要把特质混入类中,通过类重写抽象方法后,类便自动获得大量具体方法。

trait Logger{

  def log(msg:String)

  def warn(msg:String) { log(“server”+msg) }

  def server(msg:String) { log(“server”+msg) }

}

class week extends Logger{

  def log(msg:String){println(msg)}

  server(“HI”)

}

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

当为类添加多个互相调用的特质时,从最后一个开始进行处理。

在类中super.foo() 这样的方法调用是静态绑定的,明确是调用它的父类的 foo() 方法。

在特质中写下了 super.foo() 时,它的调用是动态绑定的。调用的实现讲在每一次特质被混入到具体类的时候才被决定。

因此,特质混入的次序的不同其执行效果也就不同。

abstract class IntQueue { def get(): Int;def put(x: Int) }

class BasicIntQueue extends IntQueue {

  private val buf = new ArrayBuffer[Int]

  def get() = buf.remove(0)

  def put(x: Int) { buf += x }

}

trait Incrementing extends IntQueue {

  abstract override def put(x: Int) {

    super.put(x + 1)

  }

}

trait Doubling extends IntQueue {

  abstract override def put(x: Int) {

    super.put(2 * x)

  }

}

object TestClient extends App {

  val queue1 = (new BasicIntQueue with Incrementing with Doubling)

  //Doubling.put(2*2)->Incrementing.put(4+1)

  queue1.put(2)

  println(queue1.get())  //result is 5

  val queue2 = (new BasicIntQueue with Doubling with Incrementing)

  //Incrementing.put(2+1)->Doubling.put(2*3)

  queue2.put(2)

  println(queue2.get())  //result is 6

}

App特质只支持单线程。而普通的main可以支持多线程。

扩展类和特质

构造一个扩展了指定类和特质的类的对象,同时拥有对象定义中给出的所有特性

 

 

 

 

 

 

 

 

 

 

 

 

Scala文件访问

scala中访问文件需要使用scala.io.Source对象。

例:读取本地文件。

def main(args: Array[String]) {

// 访问文件

val file = Source.fromFile("D:\\scala-test.txt")

for(line <- file.getLines){

    println(line)

  } 

}

若使用:

for(line <- file){

println(line)

  }

则不会一行一行的读取数据,而是一个字一个字的读取并输出。

例:读取网络文件。

def main(args: Array[String]) {

// 访问网络URL

val file = Source.fromURL("http://www.baidu.com")

for(line <- file.getLines){

      println(line)

    }

  }

Scala基础06:继承

标签:

原文地址:http://www.cnblogs.com/shijiaqi1066/p/4617926.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!