欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Scala学习笔记2——面向对象

程序员文章站 2022-06-14 22:10:01
...

Scala学习笔记2——面向对象

  • 实例

    class Person {
        var name : String = _   //占位符,暂不初始化
        val age = 10
        private[this] val gender = "male"
        //private[this]表示gender只能内部使用
    }
    
    object Basic {
        def main(args : Array[String]) {
            val p = new Person //类的构造函数没有参数时可以不写括号
            p.name = "Jack"
            println(p.name + ": " + p.age)
        }
    }
    
    • 声明类:一个源文件中可以包含很多类,并且都是public级别。

    • getter 和 setter

      • 使用var声明会自动生成set和get方法,使用val声明只会自动生成get方法。
      • 当不需要任何get和set方法,可以声明为private[this]
      • 如果字段是private,则自动生成的setter和getter也是private的,需要自己写set,get方法。
    • 构造函数 :主构造器/附属构造器

      //1.主构造器直接跟在类名后面,主构造器中的参数会被编译成字段
      //2.主构造器执行的时候,会执行类中的所有语句
      //3.如果参数声明时不带(val/var),则相当于private[this],外部没法访问
      class Person(val name : String, val age : Int){
          println("primary constructor")
          var gender : String = _
          
          //附属构造器一定是this命名,首行一定要调用已经存在的构造器
          def this(name : String, age : Int, gender : String){
              this(name,age)
              this.gender = gender
          }
      }
      def main(args : Array[String]){
          //主
          val p = new Person("Jack",20)
          println(p.name+":"+p.age)
          //附
          val p = new Person("Jack",20, "male")
          println(p.name+":"+p.gender)
      }
      
    • 继承

      class Person(val name : String, val age : Int){
          println("primary constructor")
          var gender : String = _
          val school = "buaa"
          
          def this(name : String, age : Int, gender : String){
              this(name,age)
              this.gender = gender
          }
      }
      //父类已有的字段就不用使用val或var修饰了
      Class Student(name : String, age : Int, val major : String) extends Person(name, age){
          println("subclass,major :"+major)
          //重写父类方法
          override def toString = "Override toString"
          override val school = "BUAA"
      }
      
      def main(args : Array[String]){
          //先调用父类的主构造器,再调用子类的主构造器
          val s = new Student("Justin",30,"Math")
          println(s.toString)
      }
      
  • 抽象类:

    • 类的一个或多个方法没有完整的定义
    • 声明抽象方法的时候不需要加abstract关键字,只需要不写方法体(当然类名需要abstract关键字修饰)。
    • 子类重写父类抽象方法(字段)时不需要加override。
    • 父类可以声明抽象字段(无初始值的字段)
    abstract class Person1{
        def speak
        val name : String
        var age : Int
    } 
    class Student1 extends Person1{
        def speak {
            println("speak")
    	}
        
        val name = "AAA"
        var age = 100
    }
    object Basic3 extends App{
        val s = new Student1
        s.speak
    }
    
  • 特质(trait)——对比JAVA8接口

    • 包含字段,有方法体的方法,抽象方法
    • 通过with关键字,一个类可以扩展多个特质
    trait Logger{
        /*
        def log(msg : String) {
            println("log" + msg)
        }
        */
        def log(msg : String)
    }
    trait ConsoleLogger extends Logger{
        //trait的子特性重写抽象方法不需要override
        def log(msg : String) {
            println(msg)
        }
    }
    //没有extends,不能有with
    class Test extends Logger{
        def test{
            log("xxx")
        }
    }
    object Basic3 extends App{
        val t = new Test
        t.test //输出logxxx
    }
    /////////////////////////////////
    trait ConsoleLogger{
        def log(msg : String) {
            println("save"+msg)
        }
    }
    trait MessageLogger extends ConsoleLogger{
        //继承非抽象方法需要override
        override def log(msg : String){
            println("save to bank"+ msg)
        }
    }
    abstract class Account{
        def save
    }
    class MyAccount extends Account with ConsoleLogger{
        def save{
            log("100")
        }
    }
    object Basic4 extends App{
        val acc = new MyAccount
        acc.save
        //若想对对象混入特质,如下操作
        val acc = new MyAccount with MessageLogger//(可以看成细化了trait)
        acc.save //会调用对象上的特质
    }
    
附:蛋糕模式
  • 蛋糕模式的思路是:假如A依赖B,我们用一个特质把被依赖方B包裹起来,我们可以叫它BComp,再用一个特质把依赖A方包裹起来,我们可以叫它AComp,我们会把AComp的自身类型声明为BComp, 这样我们可以在AComp中*引用BComp的所有成员,这样从形式上就实现了把B注入到A的效果。

  • 此外,两个Comp都要有一个抽象的val字段来代表将来被实例化出来的A或B。最后就是粘合各个组件,这需要第三个类,它同时继承Acomp和Bcomp,然后重写Comp里要求的val字段,来实例化A和B,这样,一切就都粘合并实例化好了。

  • 实例

    trait EngineComponent {
        trait Engine {
            private var running = false
            def start(): Unit = { /* as before */ }
            def stop(): Unit = {/* as before */ }
            def isRunning: Boolean = running
            def fuelType: FuelType
        }
        protected val engine : Engine
        protected class DieselEngine extends Engine {
            override val fuelType = FuelType.Diesel
        }
    }
    
    trait CarComponent {
        this: EngineComponent => // gives access to engine
        trait Car {
            def drive(): Unit
            def park(): Unit
        }
        protected val car: Car
        protected class HondaCar extends Car {
            override def drive() {
                engine.start()
                println("Vroom vroom")
            }
            override def park() { … }
        }
    }
    
    //tie them all together
    object App extends CarComponent with EngineComponent with FuelTankComponent with GearboxComponent {
        override protected val engine = new DieselEngine()
        override protected val fuelTank = new FuelTank(capacity = 60)
        override protected val gearBox = new FiveGearBox()
        override val car = new HondaCar()
    }
    App.car.drive()
    App.car.park()
    
  • Apply 与 单例

    class ApplyTest {
        def apply() = "Apply"
        def test{
            println("test")
        }
    }
    //object中的方法默认为静态方法(scala中没有static关键字)
    //本身就是单例
    object ApplyTest{
        def apply() = new ApplyTest
        //静态变量与静态方法
        var count = 0
        def incr = {
            count = count + 1
        }
    }
    class Basic4{
        
    }
    object Basic4 extends App{
        val a = ApplyTest()//类名加括号就已经调用了apply方法
        //类名不加括号会创建对象并运行object中不属于任何方法的句子
        val t = new ApplyTest
        println(t())//对象加括号调用的是class中的apply方法
    }
    
  • 包 package com.xx.data

    • 支持嵌套,下层可以访问上层作用域中的名称

      //play框架
      package.com.chinahadoop{
          package spark{
          	//
          }
      }
      
    • 顶部标记(不同包不能互相访问)

      package com.a
      package b
      
    • 包对象

      可以把一些公用的东西定义在包里面。

    • 包可见性

      可以在package里面定义哪些可见哪些不可见

    • 包在任何地方都可以引入,作用域至该语句所在块的末尾

      {
          import xx.xxx.xyy
      }
      
    • 重命名引入成员

      import java.util.(HashMap => JavaHashMap)
      
    • 隐藏方法

      HashMap => _
      
    • 自动引入

      java.lang._
      
    package aa.bb.cc.dd
    class XXX{
        private[dd] def spark = {}
        //则spark只能被dd包下的文件引用
    }
    
  • 模式匹配

    • 标准用法(match)

      //extends App相当于main函数
      object Basic5 extends App{
          val value = 1
          //scala中匹配成功后立刻退出,不需要手动break
          val result = value match {
              case 1 => "one"//match的返回值
              case 2 => "two"
              case _ => "other"
      	}
          
          val result2 = value match {
              case i if i==1 => "one" 
          }
          def t(obj : Any) = obj match {
              case x : Int => "Int"
              case s : String => "String"
              case _ => "other"
          }
      }
      
  • Case class(多用于模式匹配中)

    • 构造器中的每一个类型都为val
    • 不用new就可以直接产生对象(因为有apply方法)
    object CaseClassDemo extends App{
        def m(p : Person){
            p match{
                //也可以带名字匹配,例如case Teacher("ab"),只能匹配名字为ab的teacher对象
                case Teacher(_) => println("teacher")
                case Student(_) => println("student")
                case _ => println("unknown")
            }
        }
        m(Teacher("A"))
    }
    abstract class Person
    case class Teacher(name : String) extends Person
    case class Student(name : String) extends Person
    
  • 文件读取

    import scala.io.Source
    object FileDemo extends App{
        val file = Source.fromFile("/User/char.data")
        //读取网站上的内容:
        source.println("http://www.baidu.com")
        //按行打印
        for(line <- file.getLines()){
            println(line)
        }
        //按字符打印,两个println会打印两行
        for(ch <- file){
            println(ch)
        }
    } 
    
  • Option

    • Some(): 代表有确定值
    • None:没有确定值
    val m = Map(1->2)
    m.get(10) match {
        case Some(v) => println(v)
        case None => println("No such Key")
    }
    
  • String Interpolation

    s"hello"
    val b = "world"
    s"hello $b"//返回hello world 
    
  • 两个推荐的原码:

    Spark中的RDD.scala, SparkContext.scala