Scala 自学笔记2_类

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 和 setter
class Person{
  private var privateAge = 0                                                          // 设为私有并改名
  def age = privateAge
  def age_=(new Value:Int){
     if(new Value > privateAge) privateAge = newValue;                                //不能变年轻
  }
}

只带getter
class Message{
  val timeStamp = new Java.util.Date                                                  // 用 val
}

总结:, 四个选择
1、var foo                                                                            // getter 和 setter
2、val foo                                                                            // getter
3、自定义 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.BeanProperty
class Person{
  @BeanProperty var name:String = _
}
将生成四个方法:
1. name:String
2. name_=(newValue:String):Unit
3. getName() :String
4. 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.ArrayBuffer

class 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 Network
val myFace = new Network

chatter.Member 和 myFace.Member 是不同的两个类

val fred = chatter.join("Fred")
val wilm = chatter.join("Wilma")
fred.contacts += wilma                                       // ok
val barney = myFace.join("Barney")                           // 类型为myFace.Member
fred.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.ArrayBuffer

class 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 Network
val myFace = new Network

chatter.Member 和 myFace.Member 是不同的两个类

val fred = chatter.join("Fred")
val wilm = chatter.join("Wilma")
fred.contacts += wilma // ok
val barney = myFace.join("Barney")                                        // 类型为myFace.Member
fred.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.this
class 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.scala
scala -Dscala.time Hello Fred                                    //scala.time 会显示运行时间
Hello, Fred
[total 4ms]


枚举

定义一个扩展Enumeration类的对象,并以Value方法调用初始化枚举中的所有可选值
object TrafficLightColor extends Enumeration{
  val Red, Yellow, Green = value
}
val Red = Value
val Yellow = Value
val Green = Value
每次调用Value方法都返回内部类的新实例,该内部类也叫做Value.
或者 也可以向Value方法传入ID 名称
val Red = Value(0, "Stop") 
val Yellow = Value(10)   // 名称为Yellow
val 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.apply
TrafficLightColor.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.impatient
package people
classPerson
等同于
package com.horstmann.impatient{
  package people{
    class Person
  }
}
文件内的所有内容都属于 com.horstmann.impatient.people,但com.horstmann.impatient的包的内容是可见的,可以被直接引用

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

包可见性
package com.horstmann.impatient.people

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

引入
import java.awt.Color
import 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.

重写方法
重写一个非抽象方法必须使用 override
public class Persion extends xxx{
  override def toString = getClass.getName + "[name=" + name + "]"
}

调用超类方法和Java一样,使用super
public class Employee extends Person{
  override def toString = super.toString + "[salary=" + salary + ";"
}

类型检查和转换
isInstanceOf, asInstanceOf

if(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对象中, 所以会被自动引入

ScalaJava
obj.isInstanceOf[C1]obj instanceof C1
obj.asInstanceOf[C1](C1) obj
classOf[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 只能重写另一个def
val 只能重写另一个val 或 不带参数的def
var 只能重写另一个抽象的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
子类重写超类的抽象方法,无需override
class 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()方法返回0
env设定长度为0
Ant构造器继续执行,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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值