scala学习笔记2--类与对象,主辅构造器,伴生对象伴生类,apply方法和updte方法,继承,特质trait

1. 类与对象

1.scala中的class不存在静态成员,java中的静态数据成员由scala中的object的伴生对象替代,后面我们会详细讲到。
2.如果类中的成员属性前面什么修饰符都没有,就默认是public,外部可以访问该字段。

object ClassLearn {

  class Counter {
    var value = 0 //这里类的成员属性没有定义属性 默认情况为public

    def increment(step:Int):Unit = {
      value +=step
    }

    def current(): Int = {
      value
    }
  }

  def main(args: Array[String]): Unit = {
    val counter = new Counter
    counter.increment(5)
    println(counter.current())
  }
}

当然,这里我附带简述一下,在scala中 我们创建时候都是创建的object,而非java中的class
3.给成员变量设置为private属性时,在Java中,这是通过getter和setter方法实现的。在Scala中,也提供了getter和setter方法的实现,但是并没有定义成getXxx和setXxx。解决方案是,我们在Scala中,可以通过定义类似getter和setter的方法,分别叫做value和value _=。个人觉得这里value和value _= 只是两个函数名,换成其他名字,只要能实现相应的函数方法功能

object ClassLearn02 {
  class Test{
    private var testValue=0
    def value=testValue //定义一个方法,方法的名称就是原来我们想要的字段的名称
    def value_=(newValue:Int): Unit ={
      if(newValue>0) testValue=newValue
    }
    def increment(step:Int): Unit ={
      value+=step
    }
    def current(): Int ={
      value
    }
  }
  def main(args: Array[String]): Unit = {
    val test=new Test
    println(test.value)   //打印value的初始值
    test.value=3          //为value设置新的值
    println("new :"+test.value)
    test.increment(10)    //这里设置步长为10,每次增加10
    println(test.current())
  }
}

1.1 主构造器和辅助构造器

主构造器:每个类都有主构造器,且与类的定义交织在一起,主构造器的参数紧跟在类名之后,如 class helloworld(val hello:String,val world:String){…},主构造器的参数在传入时,就已经被编译成字段,并在构造对象时初始化传入,一个类若没有显式定义主构造器,自动拥有一个无参主构造器,若类中有直接执行的语句(非定义的方法、函数等),每次构造对象时皆会执行一次,不论是什么样的构造器类型

object ClassLearn04 {

  class View(val name: String, val ag: Int) {
    private var value = 0

    def increment(step: Int): Unit = {
      value += step
    }

    def current(): Int = {
      value
    }

    def info(): Unit = {
      printf("Name:%s and Age:%d\n", name, ag)
    }
  }

  def main(args: Array[String]): Unit = {
    val view=new View("xjh",23)
    view.info()
    view.increment(5)
    printf("Current Value is :%d\n",view.current())
  }
}

辅助构造器:Scala类可以有任意多个构造器,辅助构造器的名称为this,他在类中定义。每个辅助构造器都必须调用一个此前已经定义的辅助构造器或主构造器。

object ClassLearn03 {
  class Text{
    private var value=0
    private var name=""
    private var mode=1
    def this(name: String){   //第一个辅助构造器
      this()  //调用主构造器
      this.name=name
    }
    def this(name:String,mode:Int){ //第二个辅助构造器
      this(name)
      this.mode=mode
    }
    def increment(step:Int): Unit ={
      value+=step
    }
    def current(): Int ={
      value
    }
    def info(): Unit ={
      printf("Name :%s and mode is %d\n",name,mode)
    }
  }
  def main(args: Array[String]): Unit = {
    val text1=new Text    //主构造器
    val text2=new Text("xjh") //第一个辅助构造器
    val text3=new Text("kobe",24)   //第二个辅助构造器
    text1.info()
    text1.increment(1)
    printf("Current Value is:%d\n",text2.current())

    text2.info()
    text2.increment(2)
    printf("Current Value is:%d\n",text2.current())

    text3.info()
    text3.increment(3)
    printf("Current Value is:%d\n",text3.current())
  }
}

1.2 单例与伴生对象

scala比java更面向对象的一个方面是Scala没有静态成员(static),替代品是scala有单例对象(singleton object),当单例对象object与某个类class共享同一个名称时,他被称作是这个类的伴生对象(companion object),你必须在同一个源文件里定义类和他的伴生对象,类是这个单例对象的伴生类(companion class),类class和他的伴生对象object可以互相访问其私有成员
类和单例对象的一个差别是,单例对象object不带参数,而类class可以
不与伴生类共享名称的单例对象成为孤立对象:standlone object。最常见的就是程序入口。

/**
  * 伴生对象:当单例对象object与某个类class共享同一个名称时,他被称作是这个类的伴生对象(companion object)
  * 类class和他的伴生对象object可以互相访问其私有成员
  *
  * @author xjh 2018.10.17
  */
class TestObject private { //private声明一个私有类
  val t2 = "class :TestObject"
  var t = 123
  def func01() = {
    println("gaga01");
  }
}

object TestObject {
  val t1 = 123;
  var ssssgagag = 1444;
  val single = new TestObject();

  def func02() = {
    println("func02");
  }

  def main(args: Array[String]): Unit = {
    val t1 = new TestObject(); //实例化一个私有类对象

    println(t1.t2);
    t1.func01();

    TestObject.func02();
    println(TestObject.t1)
    println(TestObject.ssssgagag)
  }
}
/**
  * 伴生对象2 简单运用
  *因为类class和他的伴生对象object可以互相访问其私有成员
  * 这里我们在class中 调用伴生对象的方法
  * 从以下这个demo我们观察到:伴生对象中定义的newTest()方法实际实现了java中static的功能,实则 他成为了静态方法
  * 在scala中,没有static关键字,取而代之的就是伴生对象的方法 。因而 t1.id=1;t2.id=2
  * @author xjh 2018.10.17
  */
class TestObject2 {
  private val id = TestObject2.newTest() //调用了伴生对象中的方法
  private var name=""
  def this(name: String){   //辅助构造器
    this()
    this.name=name
  }
  def info(): Unit ={
    printf("The id of %s is %d\n",name,id)
  }
}

object TestObject2 {
  private var lastId = 0

  private def newTest(): Int = {
    lastId+=1
    lastId
  }

  def main(args: Array[String]): Unit = {
    val t1=new TestObject2("jiahao")
    val t2=new TestObject2("Kobe")
    t1.info()
    t2.info()
  }
}

1.3 apply方法和update方法

在scala程序编写中经常用到对象的apply方法和update方法,apply方法和update方法都会遵循相关的约定被调用,约定如下:用括号传递给变量(对象)一个或多个参数时,scala会把它转换为对apply方法的调用;与此相似的,当对带有括号并包括一到若干个参数的对象及逆行赋值时,编辑器将调用对象的update方法,在调用时,是把括号例的参数和等号右边的对象一起作为update方法的输入参数来执行调用。
举例去理解:

  1. Array(0), 取数组的第一个元素的操作会转换成 Array.apply(0) 操作,这也能解释为什么 Scala 数组取值不用中括号括下标的方式,因为它也是一次方法调用

  2. anyObject(“key1”) 会被转换成 anyObject.apply(“key”) 操作

class ApplyMethod{
  def apply(param:String): String ={
    println("apply method called,parameter is : "+param)
    "hello~"
  }
}
object ApplyMethod {
  def main(args: Array[String]): Unit = {
    val a=new ApplyMethod   //这里时创建实例化一个类ApplyMethod对象
    println(a("jiahao"))    //当在执行这条语句时,会调用类ApplyMethod中的apply方法
  }
}

运行结果:

"C:\Program Files\java\jdk1.8.0_161\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.3\lib\idea_rt.jar=1681:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\rt.jar;D:\ideaCode\LeetCode\out\production\scalaCode;C:\Program Files (x86)\scala\lib\scala-actors-2.11.0.jar;C:\Program Files (x86)\scala\lib\scala-actors-migration_2.11-1.1.0.jar;C:\Program Files (x86)\scala\lib\scala-library.jar;C:\Program Files (x86)\scala\lib\scala-parser-combinators_2.11-1.0.4.jar;C:\Program Files (x86)\scala\lib\scala-reflect.jar;C:\Program Files (x86)\scala\lib\scala-swing_2.11-1.0.2.jar;C:\Program Files (x86)\scala\lib\scala-xml_2.11-1.0.5.jar" scala02.ApplyMethod
apply method called,parameter is : jiahao
hello~

Process finished with exit code 0

我们还可以结合伴生类和伴生对象进行apply方法的使用。

class ApplyTest{
  def apply() = println("apply method in class is called!")
  def test: Unit ={
    println("Greeting method in class is called.")
  }
}
object ApplyTest{
  def apply() = {
    println("apply method in object is called")
    new ApplyTest()
  }
}

object ApplyMethod2 {
  def main(args: Array[String]): Unit = {
    val a = ApplyTest() //这里会调用伴生对象中的apply方法
    a.test
    a() // 这里会调用伴生类中的apply方法
  }
}

运行结果:

"C:\Program Files\java\jdk1.8.0_161\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.3\lib\idea_rt.jar=1720:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\rt.jar;D:\ideaCode\LeetCode\out\production\scalaCode;C:\Program Files (x86)\scala\lib\scala-actors-2.11.0.jar;C:\Program Files (x86)\scala\lib\scala-actors-migration_2.11-1.1.0.jar;C:\Program Files (x86)\scala\lib\scala-library.jar;C:\Program Files (x86)\scala\lib\scala-parser-combinators_2.11-1.0.4.jar;C:\Program Files (x86)\scala\lib\scala-reflect.jar;C:\Program Files (x86)\scala\lib\scala-swing_2.11-1.0.2.jar;C:\Program Files (x86)\scala\lib\scala-xml_2.11-1.0.5.jar" scala02.ApplyMethod2
apply method in object is called
Greeting method in class is called.
apply method in class is called!

Process finished with exit code 0

在伴生对象和apply方法结合运用时,我么可以在伴生类的apply方法中写入一些处理逻辑,这样就可以把传入的参数赋值给实例化对象的变量
在Scala中,伴生对象有一个重要用途,那就是,我们通常将伴生对象作为工厂使用,这样就不需要使用关键字new来创建一个实例化对象了

class Car(name:String){
  def info(): Unit ={
    println("Car name is "+name)
  }
}
object Car{
  def apply(name: String): Car = new Car(name)  //apply方法会调用伴生类Car的构造方法,创建一个Car类的实例化对象
}
object ApplyMethod3 {
  def main(args: Array[String]): Unit = {
    val car=Car("BMW")  //这里会调用伴生对象中的apply方法,apply方法会创建一个Car类的实例化对象
    car.info()
  }
}

了解了apply方法,我们理解update就会更加迅速。
update的规则,举个例子:
x(y) = z
这样的代码会被编译为:
x.update(y, z)

class UpdateMethod{
  def update(name:String,age:Int,address:String): Unit ={
    println("name: "+name+",age: "+age+",address: "+address)
  }
}
object UpdateMethod {
  def main(args: Array[String]): Unit = {
    val um=new UpdateMethod
    um.update("xjh",22,"changsha")
    um("xjh",22)="changsha" //这句使用update方法的特性 语句可读性变得更高

  }
}

运行结果:

"C:\Program Files\java\jdk1.8.0_161\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.3\lib\idea_rt.jar=2208:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\java\jdk1.8.0_161\jre\lib\rt.jar;D:\ideaCode\LeetCode\out\production\scalaCode;C:\Program Files (x86)\scala\lib\scala-actors-2.11.0.jar;C:\Program Files (x86)\scala\lib\scala-actors-migration_2.11-1.1.0.jar;C:\Program Files (x86)\scala\lib\scala-library.jar;C:\Program Files (x86)\scala\lib\scala-parser-combinators_2.11-1.0.4.jar;C:\Program Files (x86)\scala\lib\scala-reflect.jar;C:\Program Files (x86)\scala\lib\scala-swing_2.11-1.0.2.jar;C:\Program Files (x86)\scala\lib\scala-xml_2.11-1.0.5.jar" scala02.UpdateMethod
name: xjh,age: 22,address: changsha
name: xjh,age: 22,address: changsha

Process finished with exit code 0

我们看一下这段简短代码,伴生类里面定义了一个update方法,伴生对象可直接 调用伴生类的方法,um.update(“xjh”,22,“changsha”) 这一条语句是普通的方法调用,和其他函数方法一样,但是um(“xjh”,22)=“changsha” 运用到了update方法的特性:应用 update 时,等号右边的值会作为 update 方法的最后一个参数

1.4 重写override

重写在面向对象的语言中很常见。这里需要注意一点的是在scala中在子类中,override必须显示的写出来,java中可省略。
Scala中使用override保留字进行方法、字段重写,形如:class week extends month{ override def firstday = {…} }
override保留字实际使用类似于private,声明这个保留字后的定义、声明是对超类的重写,因此,其也可以写在类定义的参数中

1.5 继承

Scala中的继承:

  1. 重写一个非抽象方法必须使用override修饰符
  2. 只有主构造器可以调用超类的主构造器
  3. 在子类中重写超类的抽象方法时,不需要使用override关键字
  4. 可以重写超类中的字段
  5. 不允许类从多个超类继承
    extends是Scala中实现继承的保留字,形如class extends month{…},子类继承父类中所有非private成员

1.6 抽象

(1)定义一个抽象类,需要使用关键字abstract。
(2)定义一个抽象类的抽象方法,也不需要关键字abstract,只要把方法体空着,不写方法体就可以。
(3)抽象类中定义的字段,只要没有给出初始化值,就表示是一个抽象字段,但是,抽象字段必须要声明类型,比如:val carBrand: String,就把carBrand声明为字符串类型,这个时候,不能省略类型,否则编译会报错。

object AbstracrClass {
  def main(args: Array[String]): Unit = {
    var d=new Dog
    d.show()
    println("----------------------------")
    var p=new Pig
    p.show()
  }
  /**
    * 定义抽象类
    */
  abstract class Animal{
    var name:String   //抽象的var,带有一个抽象的getter方法
    var age:Int;              //抽象的var,带有抽象的getter/setter方法
    def show                  //没有方法体/函数体,是一个抽象方法
  }

  /**
    * 实现类 重写抽象方法  使用override保留字
    */
  class Dog extends Animal{
    override var name="dog";
    override var age=23
    override def show(){
      println("name :"+this.name+"\tage :"+this.age)
    }
  }

  /**
    * 实现类 重写方法 不需要使用overrride关键字
    */
  class Pig extends Animal{
    var name="pig"
    var age=20
    def show(){
      println("name :"+this.name+"\tage :"+this.age)
    }
  }
}

1.7 保护

当一个类不希望被继承、拓展时,可在类声明前加上final保留字,形如 final class year{…}
当一个类的某些成员不希望被重写时,可以在成员声明前加上final保留字,形如 class year {final def sign{…} }
当超类中的某些成员需要被子类继承,又不想对子类以外成员可见时,在成员声明前加上protected保留字,protected[this],将访问权限定于当前对象,类似于private[this]
类中protected的成员对其子类可见,对其超类不可见,形如:class year{ protected def sign {…} }
保护的应用:(注意事项)

  1. 将超类的val声明为final或者将超类的val声明为lazy
  2. 在子类中使用提前定义语法,提前定义是在超类的构造器运行之前初始化子类的字段,把需要提前定义的语句块放在extends与超类之间,并后接with保留字

1.8 特质trait

Scala的特质Trait相当于Java中的接口,不过特质中可以有实现的方法
在Scala中实现特质被成为混入,混入的第一个特质用关键字extends,混入更多的用with
Java中提供了接口,允许一个类实现任意数量的接口。在Scala中没有接口的概念,而是提供了“特质(trait)”,它不仅实现了接口的功能,还具备了很多其他的特性。Scala的特质,是代码重用的基本单元,可以同时拥有抽象方法和具体方法。Scala中,一个类只能继承自一个超类,却可以实现多个特质,从而重用特质中的方法和字段,实现了多重继承。

trait CarId{
  var id: Int
  def currentId(): Int     //定义了一个抽象方法
  def test(msg:String): Unit ={
    println(msg)
  }
}
class BYDCarId extends CarId{ //使用extends关键字
  override var id = 10000 //BYD汽车编号从10000开始
def currentId(): Int = {id += 1; id} //返回汽车编号
}
class BMWCarId extends CarId{ //使用extends关键字
  override var id = 20000 //BMW汽车编号从10000开始
def currentId(): Int = {id += 1; id} //返回汽车编号
}
object TraitLearn {
  def main(args: Array[String]): Unit = {
    val myCarId1 = new BYDCarId()
    val myCarId2 = new BMWCarId()
    printf("My first CarId is %d.\n",myCarId1.currentId)
    myCarId1.test("123")
    printf("My second CarId is %d.\n",myCarId2.currentId)
    myCarId2.test("abcd")
  }
}

参考文献:子雨大数据之Spark入门教程(Scala版)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值