Scala 自学笔记2_类

来源:互联网 发布:js开发工具 编辑:程序博客网 时间:2024/05/29 10:46

1、类

class Counter{  private var value = 0                                                               // 必须初始化字段  def increment() { value += 1}                                                       //方法默认是公有的  def corrent() = value}                                                                                      //类无需声明为public , 源文件可以包含多各类,所有类都具有公有可见性。val myCounter = new Counter                                                           // 或 new Counter()myCounter.increment                                                                                       //或 myCounter.increment() ,对于不会改变对象状态的方法,去掉()是不错的风格                                                                                      //可以通过以不带()的方式声明来强制这种风格class Counter {  ...  def current = value                                                                 // 定义中不带()}                                                                                      //这样 使用者必须用 myCounter.current

getter 和 setter

class Person{  var age = 0                                                                         // 公有,Scala 默认生成 get和set, 分别为 age 和 age_=                                                                                      //如果定义为私有,那么 get 和 set 也将变为私有}println(fred.age)                                                                     //  调用了fred.age()fred.age = 21                                                                         //调用了 fred.age_(21) 自定义getter 和 setterclass Person{  private var privateAge = 0                                                          // 设为私有并改名  def age = privateAge  def age_=(new Value:Int){     if(new Value > privateAge) privateAge = newValue;                                //不能变年轻  }}只带getterclass Message{  val timeStamp = new Java.util.Date                                                  // 用 val}总结:, 四个选择1、var foo                                                                            // getter 和 setter2、val foo                                                                            // getter3、自定义 foo 和 foo_= 方法4、自定义 foo 方法

对象私有字段

class Counter{  private var value = 0  def increment() { value += 1}  def isLess(other: Counter) = value < other.valule                 // 可以访问另一个对象的 私有字段}                                                                   // 之所以访问other.value是合法的,是因为other也同样是Counter对象更严格的访问限制, 通过private[this] 修饰来实现private [this] var value = 0                                        //如此一来 Counter类的方法只能访问到当前对象的value字段,而不能访问同样是Counter类型的其他对象的该字段。类私有字段 生成私有getter 和 setter, 但对象私有的字段,不会生成。

Bean属性

虽然Scala会提供getter和setter方法,但这不是java工具所预期的,,如果要更具JavaBeans规范生成getFoo/setFoo方法,参照如下:import scala.reflect.BeanPropertyclass Person{  @BeanProperty var name:String = _}将生成四个方法:1. name:String2. name_=(newValue:String):Unit3. getName() :String4. setName(newValue:String):Unit如果使用主构造器参数的方式定义了某字段,并且需要JavaBean版的getter 和 setter 的方法:class Person(@BeanProperty var name:String)

辅助构造器

class Person{  private var name = ""  private var age = 0    def this(name: String){    this()                                                         //调用主构造器    this.name = name  }  def this(name: String, age:Int){    this(name)                                                     //调用前一个辅助构造器    this.age = age  }}val p1 = new Person                                                //主构造器val p2 = new Person("Fred")                                        // 第一个辅助构造器val p3 = new Person("Fred", 42)                                    // 第二个辅助构造器

主构造器

主构造器的参数直接放置在类名之后import scala.collection.mutable.ArrayBufferclass Networ{  class Member(val name: String){    val contacts = new ArrayBuffer[Member]  }  private val members = new ArrayBuffer[Member]  def join(name:String) = {    val m = new Member(name)    members += m    m  }}val chatter =  new Networkval myFace = new Networkchatter.Member 和 myFace.Member 是不同的两个类val fred = chatter.join("Fred")val wilm = chatter.join("Wilma")fred.contacts += wilma                                       // okval barney = myFace.join("Barney")                           // 类型为myFace.Memberfred.contacts += barney                                      //错误,不能将一个myFace.Member 添加到chatter.Member元素缓冲中如果希望突破这个限制,可以将嵌套类移到其他地方,一个不错的位置是 伴生对象object Network{  class Member(val name:String){}                            //类似java的静态对象,挂在类上,而不是 对象上。}class Network{  private val members = new ArrayBuffer[Network.Member]}或者是投影class Network {  class Member(val name: String) {    val contacts = new ArrayBuffer[Network#Member]  }}class Person(val name:String , val age:Int){}主构造器会执行类定义中的所有语句class Person(val name:String, val age: Int){   println("Just constructed another person")   def description = name + " is " + age + " years old"}class MyProg{   private val props = new Properties   props.load(new FileRecoder("myprog.properties"))}class Person(val name:Stirng , private var age:Int)class Person(name:String, age:Int) { // 这里没有给val 或 var修饰,但是在它们至少被一个方法使用,所以升格为字段,并对象私有,同等于private[this] val字段   def description = name + " is " + age + " years old" }//如果它们没被使用,将不保存为字段,仅仅是主构造器中的代码访问的普通参数。主构造器参数生成的字段和方法name: String对象私有字段。如果没有方法使用name,则没有该字段private val/var name: Sting私有字段,私有的getter/setter方法val/var name:String私有字段,公有的getter/setter方法@BeanProperty val/var name:String私有字段,公有的Scala版和JavaBeans版的getter/setter方法私有的主构造器class Person private(val id:Int) {} //这样 用户必须通过辅助构造器了



嵌套类

import scala.collection.mutable.ArrayBufferclass Networ{  class Member(val name: String){    val contacts = new ArrayBuffer[Member]  }  private val members = new ArrayBuffer[Member]  def join(name:String) = {    val m = new Member(name)    members += m    m  }}val chatter =  new Networkval myFace = new Networkchatter.Member 和 myFace.Member 是不同的两个类val fred = chatter.join("Fred")val wilm = chatter.join("Wilma")fred.contacts += wilma // okval barney = myFace.join("Barney")                                        // 类型为myFace.Memberfred.contacts += barney                                                   //错误,不能将一个myFace.Member 添加到chatter.Member元素缓冲中如果要突破这个先知,可以将嵌套类移到别处,一个不错的选择是 伴生对象object Network{  class Member(val name:String){}}class Network{  private val members = new ArrayBuffer[Network.Member]}或者是使用 类型投影Network#Member,其含义是“任何Network的Member”class Network{  class Member(val name:String){    val contacts = new ArrayBuffer[Network#Member]  }}<pre name="code" class="java">在嵌套类中,可以通过外部类.this 的方式来访问外部类的this引用.class Network(val name:String) { outer ->                  // outer 变量指向 Network.thisclass Member(val name:String){    ...    def description = name + "inside " + outer.name   }}


2、对象

单例对象
Scala没有静态方法或静态字段,可以用objectg 这个语法来达到相同的目的object Accounts {  privagte var lastNumber = 0  def newUniqueNumber() = { lastNumber += 1; lastNumber }<pre name="code" class="java">}Account.newUniqueNumber()对象的构造器在该对象第一次被使用时调用,这里即首次调用newUniqueNumber时执行,如果对象从未被使用,那么其构造器不会被执行对象可以拥有类的所有特性, 可以扩展其他类或特质,唯一的例外就是:不能提供构造器参数常用的方式与 Java中使用单例的地方相似:1、存放工具函数或常量的地方2、高效地共享单个不可变实例3、需要用单个实例来协调某个服务时(单例模式)

伴生对象

在java中,可以既有实例方法又有静态方法,在Scala,就是伴生对象来完成:

class Accout{  val id = Account.newUniqueNumber()  private var balance = 0.0  def deposit(amount: Double) { balance += amount}  ...}object Account {                                                       //伴生对象  private var lastNumber = 0  private def newUniqueNumber() = { lastNumber +=1; lastNumber}}类和它的伴生对象可以互相访问私有特性,必须存在于同一个源文件中

扩展类和特质的对象

abstract class UndoableAction(val description: String){  def undo(): Unit  def redo(): Unit}object DoNothingAction extends UndoableAction("Do nothing"){  override def undo(){}  override def redo(){}}DoNothingAction 对象可以被所有需要这个缺省行为的地方共有。val actions = Map("open" -> DoNothingAction, "save" -> DoNothingAction, ...)//打开和保存功能尚未实现

apply方法

通常使用类的伴生对形象的apply方法来初始化类,省去new 关键字object(参数1, ...,  参数n)Array("Mary","had","a","little","lamb")Array(Array(1,7), Array(2,9))class Account private (val id:Int, initialBalance: Double){  private var balance =initialBalance} object Account{  def apply(initialBalance : Double) = new Account(newUniqueNumber(), initialBalance)  ... }val acct = Accounnt(1000.0)应用启动对象每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为Array[String] => Unit:object Hello{  def main(args:Array[String]){    println("Hello, World!")  }}<pre name="code" class="java">也可以扩展App特质object Hello extends App{  prinltn("Hello, World!")}如果需要命令行参数,可以通过args属性得到object Hello extends App{  if(args.length > 0)    println("Hello, " + argss(0))  else    println("Hello, World!")}scalac Hello.scalascala -Dscala.time Hello Fred                                    //scala.time 会显示运行时间Hello, Fred[total 4ms]


枚举

定义一个扩展Enumeration类的对象,并以Value方法调用初始化枚举中的所有可选值object TrafficLightColor extends Enumeration{  val Red, Yellow, Green = value}val Red = Valueval Yellow = Valueval Green = Value每次调用Value方法都返回内部类的新实例,该内部类也叫做Value.或者 也可以向Value方法传入ID 名称val Red = Value(0, "Stop") val Yellow = Value(10)   // 名称为Yellowval Green = Value("Go")// id 为11如果不指定ID,则ID在前一个枚举值基础上加一,从零开始,缺省名称 为字段名。可以用TrafficLightColor.Red, 等来引用,如果觉得太冗长,可以直接引入枚举值import TrafficLightColor._
也可以用一种类型别名:object TrafficLightColor extends Enumeration{  type TrafficLightColor = Value  val Red, Yellow, Green = Value}现在枚举的类型变成了TrafficLightColor.TrafficLightColor,不过仅当使用import 语句这样做才有意义。import TrafficLightColor._def doWhat(color: TrafficLightColor) ={  if( color == Red) "stop"  else if (color == Yellow) "hurry up"  else "go"}
枚举值的ID 可以通过id 方法返回,名称通过toString方法返回for(c <- TrafficLightColor.values) println( c.id = ": "+ c)可以通过枚举的ID或名称来进行查找定位,以下两段代码都输出TrafficLightColor.Red对象TrafficLightColor(0) // 将调用Enumeration.applyTrafficLightColor.withName("Red")

3、包和引入

package com{  package horstmann{    package impatient{      class Employee      ...    }  }}packkage com.horstmann.impatient{                                  //串联式,com和com.horstmann的成员在这里不可见  package people{     classPerson  }}源文件的目录和包之间并没有强制的关联关系,不需要将Employee.scala 放在 com/horstmann/impatient目录当中。同时也可以在一个文件当中定义多个包。



作用域规则
package com{  package horstmann{    object Utils{      def percentOf() = ...    }    package impatient{      class Employee{        ...        def giveRaise(rate: scala.Double){          salary += Utils.percentOf(salary, rate)        }      }    }  }}这里Utils.percentOf使用的是当前包下的内容,是相对路径,而Java一直是 绝对路径注意:如果有个包定义为collection , 而在使用scala的collection时,使用相对路径,可能报错,这个时候就要使用绝对路径,以_root_开始,如val subordinates = new _root_.scala.collection.mutable.ArrayBuffer[Employee]

文件顶部标记法
package com.horstmann.impatientpackage peopleclassPerson等同于package com.horstmann.impatient{  package people{    class Person  }}文件内的所有内容都属于 com.horstmann.impatient.people,但com.horstmann.impatient的包的内容是可见的,可以被直接引用

包对象
包可以包含类、对象和特质,但不能包含函数或变量,包对象就是为了解决这个局限。package com.hostmann.impatientpackage object people{  val defaultName = "John"}package people{  class Person{    var name = defaultName                                           // 从包对象拿到常量  }}

包可见性
package com.horstmann.impatient.peopleclass Person{  private [people] def description = "A person with name" + name  ...}// description在people 包内可见可以将可见度 延展到上层包private [impatient] def description = ...

引入
import java.awt.Colorimport java.awt._                                               //等同于java的通配符*import java.awt.{Color, Font}                                   //选择性引入import java.util.{HashMap => JavaHashMap}                       //重命名import java.util.{HashMap=>_,_}                                 //隐藏HashMap,引入其他成员任何地方都可以声明引入class Manager{  import scala.collection.mutable._}
隐式引入每个Scala程序都隐式地如下代码import java.lang._import scala._import Predef._后面引入会覆盖前面重名的,例如scala.StringBuilder 会覆盖 java.lang.StringBuilder 而不是与之冲突。当 我们 使用 collection.mutable.Hashmap其实是在使用scala.collection.mutable.Hashmap

4、继承

使用extends关键字class Employee extends Person{  var salary = 0.0}final 类无法被继承,final方法无法被重写,final字段无法被覆盖。注: java的final字段是不可变,scala 即为 val.

重写方法
重写一个非抽象方法必须使用 overridepublic class Persion extends xxx{  override def toString = getClass.getName + "[name=" + name + "]"}调用超类方法和Java一样,使用superpublic class Employee extends Person{  override def toString = super.toString + "[salary=" + salary + ";"}

类型检查和转换
isInstanceOf, asInstanceOfif(p.isInstanceOf[Employee]){  val s = p.asInstanceOf[Employee]                                           // s的类型为Employee}如果p是 Employee类或其自雷的对象,isInstanceOf 将会成功如果p是null,则p.isInstanceOf[Employee]返回false,  p.asInstanceOf[Employee]返回null如果p不是一个Employee , 则 p.asInstanceOf[Employee] 将跑出异常如果想要测试p指向的 是一个Employee对象,但又不是自雷的话,用p.getClass == classOf[Employee]                                              // classOf定义在 scala.Prede对象中, 所以会被自动引入
ScalaJavaobj.isInstanceOf[C1]obj instanceof C1obj.asInstanceOf[C1](C1) objclassOf[C1]C1.class
不过,与类型检查和转换相比, 模式匹配通常是更好的选择:p match{  case s: Employee => ...                                                    // 将s 作为Employee 处理  case _ => // p 不是Employee}

受保护字段和方法
和Java一样, protected, 可以被任何子类访问,但不能从其他位置看到。与Java不同,protected的成员对于类所属的包而言,是不看见的, 如果需要这种可见性,可以使用 包修饰:protected [包名] def xxx = xxx;

超类的构造
class Employee(name: String, age: Int, val salary: Double) extends Person(name, age)这里定义了子类 和 调用超类的主构造器。在Scala的构造器中,不能像Java一样 ,调用super(params)。Scala类可以扩展Java类class Square(x:Int, y:Int, width:Int) extends java.awt.Rectangle(x, y, width, width)

重写字段
def 只能重写另一个defval 只能重写另一个val 或 不带参数的defvar 只能重写另一个抽象的var                               //换句话说,如果你用了var, 所有的子类只能被动接受class Person(val name: String){  override def toString = getClass.getName + "[name=" + name + "]" }class SecretAgent(codename :String ) extendsd Person(codename){  override val name = "secret"                           // 覆盖了父类的name  override val toString = "secret"}abstract class Person{  def id:Int}class Student ( override val id : Int) extends Person    // 覆盖了父类的 id

匿名子类
val alien = new Person("Fred") {  def greeting = "Greetings, Earthling! My name is Fred."}从技术上讲,这将会创建一个结构类型的对象, 该对象标记为 Person{def greeting : String}可以用这个类型 作为 参数类型的 定义def meet(p : Person ( def greeting: String)){  println(p.name + "says: "+ p.greeting)}

抽象类
abstract class Person (val name : String) {  def id :Int                                                   // 没有方法体, 抽象方法,无需像java一样,加上abstract}类中如果有一个方法是 抽象的,那么该类必须声明为abstract子类重写超类的抽象方法,无需overrideclass Employee(name:String) extends Person(name){  def id = name.hashCode                                        // 不需要override}

抽象字段
没有初始值的字段,即为抽象字段abstract class Person{  val id : Int                                                 //带有抽象的getter方法  var name: String                                             //带有抽象的 g/s}class Employee (val id: Int) extends Person {                  //  具体的 id  var name = ""                                                //具体的 name , 同样无需 override}用 匿名类型 来 定制 抽象字段:val fred = new Person{   val id = 1729  var name = "Fred"}

构造顺序和提前定义

class Creature {  val range: Int = 10  val env:Array[Int] = new Array[Int](range)}class Ant extends Creature{  override val range = 2}执行流程:range 设置为10初始化env数组,调用range()取值器该方法被重写以输出(还未初始化的)Ant类的range字段值range()方法返回0env设定长度为0Ant构造器继续执行,range字段为2这里的问题是在构造器内不应该依赖val的值。这里和Java一样,当在超类的构造方法中调用方法时,会遇到相似的问题,被调用的方法可能被子类重写,因此它可能并不会按照你的语气行事。解决方法:1、val 声明为final , 安全不灵活2、超类中val声明为lazy, 安全不高效3、子类提前定义语法class Ant extends {  override val range = 2                                          // 在超类构造器执行之前初始化子类的val字段}with Creature

Scala继承层级

与Java中基本类型相对应的类,以及Unit类型,都扩展自AnyVal所有其他类都是AnyRef的子类,AnyVal和AnyRef都扩展自Any类Any类定义了 isInstanceOf、asInstanceOf方法,以及用于相等性判断和哈希码的方法。AnyRef类追加了来自Object类的wart和notify/notifyAll,同时提供了一个带函数参数的方法synchronized,等同于Java中的synchronized块,如account.synchronized{account.balanced += amount}





对象相等性
AnyRef的 eq 方法检查两个引用是否指向同一个对象,AnyRef的equals方法调用eq。在应用程序中,不直接调用eq或equals,只要用==操作符就好override def equals(other : Any) ={  val that = other.asInstanceOf[Item]  if(that == null) false  else description == that.description && price == that.price}override def hashCode = 13 * description.hashCode + 17 * price.hashCode //定义equals时, 记得同时也定义hashCode







0 0
原创粉丝点击