美文网首页
Scala 类和对象

Scala 类和对象

作者: wangdy12 | 来源:发表于2018-03-19 10:31 被阅读0次

    如果类没有内容(body),可以省略空的花括号,只写一行语句

    class User
    val user1 = new User
    

    类可以带参数,类名称C之后圆括号内的参数,x称为类参数class parameter,Scala编译器会使用类参数创建出一个主构造函数paimary constructor

    class C(x: R)
    

    私有主构造函数,只在被类本身以及伴生对象访问

    class class C(private x: R) 
    

    Scala编译器会将编译类内部的任何代码,如果代码不是字段定义和方法定义,则被编译到主构造函数中
    可以用_对成员初始变量进行初始化,对应为0或者null

    class C(var x: R) {
      assert(x > 0, "positive please") //对参数的合法性进行检验
      var y = x
      val readonly = 5
      private var secret = _
      def this() = this(42)
    } 
    

    assert 方法位于scala.Predef对象内

    辅助构造函数
    主构造函数之外的构造函数称为辅助构造函数,辅助构造函数的第一个条语句必须是调用同类的其他构造函数,所有每个Scala构造器最终都会调用主构造器

      def this() = this(42)
    

    私有构造函数
    在类名称和参数列表之间添加关键字private/protected,来声明私有的或受保护的构造函数

    class C private (var x: R)
    

    隐式转换
    implicit 修饰符告诉编译器在某些情况下自动应用它,它必须定义在作用域范围之内

    implicit def intToRational(x: Int) = new Rational(x)
    

    创建一个隐式转换,在需要时自动将整数转换为有理数


    抽象类

    使用abstract来声明

    abstract class D { ... }
    

    继承
    子类拥有父类非private的方法,如果不显示指明继承,默认继承scala.AnyRef,与java.lang.Object等同

    class C extends D { ... }
    

    方法重载
    override关键词,进行函数重载,如果子类重写了父类的具体函数,必须添加override修饰符,否则就会出现编译错误

    在Scala中,字段和方法属于同一个名称空间,可以使用字段覆盖无参数的方法

    abstract class Element {
        def contents: Array[String]
    }
    
    class ArrayElement(conts: Array[String]) extends Element {
        val contents: Array[String] = conts
    }
    

    Java有四个名称空间是字段,方法,类型和包,相比之下,Scala有两个命名空间namespace

    • 值(字段,方法,包和单例对象)
    • 类型(类和特质名称)
      因此可以用字段覆盖无参数的方法,同一类内部的字段和方法名称不能相同

    调用超类的构造函数
    传递参数给超类的主构造函数,把参数放到超类名称后的括号内即可

    class D(var x: R)
    class C(x: R) extends D(x)
    

    参数化字段 parametric field

    一种简写方式,用于组合参数和字段,同时定义同名的参数和字段

    //这里的conts只是构造函数的参数,类没有对应的字段
    class ArrayElement(conts: Array[String]) extends Element {  
        val contents: Array[String] = conts
    }
    // 简写,自动生成同名的字段
    class ArrayElement(
        val contents: Array[String]
    ) extends Element
    

    参数前可以是valvarprivateprotectedoverried之类的修饰符

    class Cat {
      val dangerous = false
    }
    class Tiger(
        override val dangerous: Boolean,
        private var age: Int
    ) extends Cat
    //等价于
    class Tiger(param1: Boolean, param2: Int) extends Cat {
        override val dangerous = param1
        private var age = param2
    }
    

    实际上如在出构造函数之外的地方使用构造参数,内部依然会把它转换为private val类型的字段

      class Foo(bar: Int){
        def funtion1(): Unit ={
          println(bar)
        }
      }
    

    内部包含字段private val bar: Int,与class Foo(private val bar: Int)的区别在于没有对应的私有getter方法private int bar();生成


    变量的Getter/Setter方法

    Scal字段会自动生成getter和setter方法,val类型没有setter方法
    Scala支持setter方法的名称为getter函数名后加_=,使用时可以对getter函数名直接赋值

    class Test{
      private var _x = 0
    
      def x = _x //get方法
      def x_= (newValue: Int): Unit = { //set方法
        _x = newValue
      }
    }
    
    //使用方法
    val test = new Test
    test.x = 5
    val x = test.x
    

    Singleton Objects

    Scala没有static成员,通过单例对象来实现类似功能,等同于把所有的静态成员都放到了单例对象中,可以通过名称直接调用它的方法,就像一个静态方法的集合

    • 它的定义类似于一个类的定义,不同的是它的关键字是object
    • object是只有一个实例的类,本身可以看做是一个惰性加载的val类型
    • 实际实现是一个合成类(synthetic class)实例,被类成员中的一个静态变量引用,编译产生的类名是对象名称加上美元符号SingletonObjectName$

    单例对象和类的一个不同在于单例对象不能接收参数,而类可以。因为不能使用 new 来实例化一个单例对象,因此没有办法给它传递参数

    Companion object

    单例对象的名字和类名称一致的时候叫做伴生对象companion object,必须在同一个文件中定义类和它的伴生对象,他们可以互相访问对方的私有成员

    standalone object

    没有和类共享名称的单例对象叫做独立对象,为了运行一个Scala程序,必须提供一个独立对象,内部包含一个程序执行的入口函数def main(args: String) = {}

    Scala每个源文件都默认包含java.langscala包,以及scala.Predef的单例对象
    Predef中包含了许多有用的方法, 例如,printlnassert方法
    同时Scala不要求 .scala文件名称对应内部的类,可以随意命名

    通过混入scala.App类型的trait,定义应用程序对象,即在对象后添加extends App,放置在main方法中的代码直接放在单体对象的花括号之间,通过args字符串数组访问命令行参数即可


    特质trait

    封装字段和方法定义,类似Java的接口,使用extends或者with关键字,混入(mixin)到类中,数量没有限制
    使用extends关键词,类隐式继承特质的超类(特质没有声明超类时,默认为AnyRef
    如果类已经显示扩展超类,使用with

    • trait不能有类参数(传递到类的主构造函数的参数)
    • 超类调用是不确定的,动态变化的(trait超类的调用方式)
    trait T1; trait T2
    class C extends T1 with T2
    class C extends D with T1 with T2
    

    特质不仅可以有抽象方法,也可以包含具体方法

    trait Ordered[A] extends Any with java.lang.Comparable[A] {
      def compare(that: A): Int
      def <  (that: A): Boolean = (this compare that) <  0
      def >  (that: A): Boolean = (this compare that) >  0
      def <= (that: A): Boolean = (this compare that) <= 0
      def >= (that: A): Boolean = (this compare that) >= 0
      def compareTo(that: A): Int = compare(that)
      def compareTo(that: A): Int = compare(that)
    }
    

    stackable modification 可堆叠的变化

    特点

    • 特质指定超类,那么只能用于同样扩展该超类的类
    • 特质的抽象方法调用超类,特质中的super调用是动态绑定的,混入另一个特征或类后,才有具体的定义

    abstract override修饰符组合,只能由于特质,它表明特质必须混入到有具体方法定义的类中

    abstract class IntQueue {
      def get(): Int
      def put(x: Int)
    }
    
    import scala.collection.mutable.ArrayBuffer
    class BasicIntQueue extends IntQueue {
      private val buf = new ArrayBuffer[Int]
      def get() = buf.remove(0)
      def put(x: Int) = { buf += x }
    }
    
    trait Doubling extends IntQueue {
      abstract override def put(x: Int) = { super.put(2 * x) }
    }
    
    trait Incrementing extends IntQueue {
      abstract override def put(x: Int) = { super.put(x + 1) }
    }
    trait Filtering extends IntQueue {
      abstract override def put(x: Int) = {
        if (x >= 0) super.put(x)
      }
    }
    
    val queue = (new BasicIntQueue with Filtering with Incrementing)
    

    多个traits重载同一个方法,不同的次序会产生不同的类,优先级是从右向左,以类似栈的形式调用函数
    类似多重继承,是利用线性化linearization算法解释超类

    当用new实例化一个类时,Scala把这个类及其所有继承的类和特质以线性次序放在一起。 然后,每当其中一个类中调用super时,被调用的方法就是链中的下一个。 如果除最后一次调用之外的所有方法都调用super,那么最终结果就是是可堆叠行为

    相关文章

      网友评论

          本文标题:Scala 类和对象

          本文链接:https://www.haomeiwen.com/subject/mpqtextx.html