Scala 类和对象

2018-03-19  本文已影响0人  wangdy12

如果类没有内容(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成员,通过单例对象来实现类似功能,等同于把所有的静态成员都放到了单例对象中,可以通过名称直接调用它的方法,就像一个静态方法的集合

单例对象和类的一个不同在于单例对象不能接收参数,而类可以。因为不能使用 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 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 可堆叠的变化

特点

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,那么最终结果就是是可堆叠行为

上一篇下一篇

猜你喜欢

热点阅读