快学Scala-第五章 类

知识点:

1.简单类和无参方法

class Counter {
   private var value = 0 //必须初始化字段
   def increment() { value += 1} //方法默认是公有的
   def current() = value
}

在Scala中,类并不声明为public,Scala源文件可以包含多个类,所有这些类都具有共有可见性。

val myCounter = new Counter //or new Counter()
myCounter.increment()
println(myCounter.current())

调用无参方法,可以写上圆括号,也可以不写。对于改值器方法(即改变对象状态的方法)使用(),对于取值器方法(不会改变对象状态的方法)去掉().可以通过以不带()的方式声明current来使用不带圆括号的风格。

2.带gettter和setter的属性

Scala生成面向JVM的类,对于声明的字段都会提供getter和setter方法。在Scala中,getter和setter分别叫做age和age_=,可以重新定义getter和setter方法。

  • 对于私有字段而言,getter和setter方法也是私有的;
  • 如果字段是val, 则只有getter方法被生成;
  • 如果你需要任何getter和setter,可以将字段声明为 private[this].

颇具影响的Eiffel语言的发明者Bertrand Meyer提出了“ 统一访问原则”:某个模块提供的所有服务都应该能通过统一的表示法访问到,至于它们是通过存储还是通过计算来实现的,从访问方式上应无从可知。Scala符合这个原则。

3.只带getter的属性

如果属性的值在对象构建完成后不再改变,则可以使用val字段,scala会生成一个私有的final字段和getter方法。总结一下,在实现属性时有如下四个选择:

  • var foo: Scala自动合成一个getter和setter方法
  • val foo: Scala自动合成一个getter
  • 由你来定义foo和foo_=方法
  • 由你来定义foo方法

当你在Scala类中看到字段的时候,它代表的是一个私有字段加上getter方法(对val字段)或者getter和setter方法(对var字段而言)。

4.对象私有字段

private[this]修饰字段时,表明某个对象.value这样的访问不被允许。类定义的方法只能访问到当前对象的value字段,而不能访问同样类的其他对象的字段,这样的访问被称为对象私有的。

对于类私有的字段,Scala生成私有的getter和setter方法,而对于对象私有的字段,Scala不会生成getter和setter方法。

5.Bean属性

将Scala字段标注为@BeanProperty时,会自动生成getFoo/setFoo这样的方法。

Scala字段 生成的方法 何时使用
val/var name 公有的name

name_=(仅限于var)

实现一个可以被公开访问并且背后是以一个字段形式保存的属性
@BeanProperty val/var name 公有的name

getName()

name_=(仅限于var)

setName(…) (仅限于var)

与JavaBean互操作
private val/var name 私有的name

name_=(仅限于var)

用于将字段访问限制在本类的方法,就和Java一样,尽量使用private——除非你真的需要一个公有的属性
private[this] val/var name 用于将字段访问限制在同一个对象上调用的方法,并不经常用到
private[类名] val/var name 依赖于具体实现 将访问权赋予外部类,不经常用到

6.辅助构造器

1)辅助构造器的名称为this

2)每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或猪狗在其的调用开始。

如果没有显示定义主构造器则自动拥有一个无参的主构造器。

class Person{
  private var age = 0
  private var name = ""

  def this(name:String){
    this()
    this.name=name
  }

  def this(name:String,age:Int){
    this(name)
    this.age = age
  }
}

7.主构造器

1)主构造器的参数直接放置在类名后

2)主构造器会执行类定义中的所有语句

如果类名之后没有参数,则该类具备一个无参主构造器。通常可在主构造器中使用默认参数来避免过多的使用辅助构造器。构造参数可以是普通的方法参数,不带val或var,这样的参数如何处理取决于它们在类中如何被使用。如果不带val或var的参数至少被一个方法所使用,它将被升格为字段;否则,该参数将不被保存为字段,仅仅是一个可以被主构造器中的代码访问的普通参数。

class Person(name:String,age:Int){
  def description = name + " is " + age + " years old"
}
//声明和初始化了不可变字段name、age,这两个字段都是对象私有的,等同于private[this] val字段的效果

针对主构造器参数生成的字段和方法

主构造器参数 生成的字段/方法
name: String 对象私有字段,如果没有方法使用那么,则没有该字段
private val/var name: String 私有字段,私有的getter/setter方法
val/var name: String 私有字段,公有的getter/setter方法
@BeanProperty val/var: String 私有字段,公有的Scala版和JavaBean版的getter/setter方法

8.嵌套类

在Scala中,几乎可以在任何语法结构中内嵌任何语法结构,在任何函数中定义函数,在类中定义类。



练习:(参考网址

1.改进5.1节的Counter类,让它不要在Int.MaxValue时变成负数

class Counter {
   private var value = 0 //必须初始化字段
   def increment() {
     if(value < Int.MaxValue){
       value += 1
     }
     else value

   } //方法默认是公有的
   def current() = value
}

2.编写一个BankAccount类,加入deposit和withdraw方法,和一个只读的balance属性

class BankAccount{
  val balance = 0
  def deposit(amount:Double) {}
  def withdraw(){}
}

3.编写一个Time类,加入只读属性hours和minutes,和一个检查某一时刻是否早于另一时刻的方法before(other:Time):Boolean。Time对象应该以new Time(hrs,min)方式构建。其中hrs以军用时间格式呈现(介于0和23之间)

class Time(val hours:Int,val minutes:Int){

   def before(other:Time):Boolean={
     hours < other.hours || (hours == other.hours && minutes < other.minutes)
   }

   override def toString():String={
     hours + " : " + minutes
   }
}

4.重新实现前一个类中的Time类,将内部呈现改成午夜起的分钟数(介于0到24*60-1之间)。不要改变公有接口。也就是说,客户端代码不应因你的修改而受影响

class Time(val hours:Int,val minutes:Int){

   def before(other:Time):Boolean={
     hours < other.hours || (hours == other.hours && minutes < other.minutes)
   }

   override def toString():String={
     (hours * 60 + minutes)+""
   }
}

5.创建一个Student类,加入可读写的JavaBeans属性name(类型为String)和id(类型为Long)。有哪些方法被生产?(用javap查看。)你可以在Scala中调用JavaBeans的getter和setter方法吗?应该这样做吗?

import scala.beans.BeanProperty
//答案里导入的包 scala2.11.8没有这个类了,在API中查找包位置
class Student {
  @BeanProperty var name:String = _
  @BeanProperty var id:Long = _
}

javap –c Student 查看 生成了name()\name_=\setName()\getName()   id()\id_=\setId()\getId()

Compiled from "Student.scala"
public class Student {
  public java.lang.String name();
    Code:
       0: aload_0
       1: getfield      #15                 // Field name:Ljava/lang/String;
       4: areturn

  public void name_$eq(java.lang.String);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #15                 // Field name:Ljava/lang/String;
       5: return

  public void setName(java.lang.String);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #15                 // Field name:Ljava/lang/String;
       5: return

  public long id();
    Code:
       0: aload_0
       1: getfield      #24                 // Field id:J
       4: lreturn

  public void id_$eq(long);
    Code:
       0: aload_0
       1: lload_1
       2: putfield      #24                 // Field id:J
       5: return

  public void setId(long);
    Code:
       0: aload_0
       1: lload_1
       2: putfield      #24                 // Field id:J
       5: return

  public java.lang.String getName();
    Code:
       0: aload_0
       1: invokevirtual #30                 // Method name:()Ljava/lang/String;
       4: areturn

  public long getId();
    Code:
       0: aload_0
       1: invokevirtual #33                 // Method id:()J
       4: lreturn

  public Student();
    Code:
       0: aload_0
       1: invokespecial #37                 // Method java/lang/Object."<init>":
()V
       4: return
}

6.在5.2节的Person类中提供一个主构造器,将负年龄转换为0

class Person(var age:Int){
  age = if(age < 0) 0 else age
}

7.编写一个Person类,其主构造器接受一个字符串,该字符串包含名字,空格和姓,如new Person("Fred Smith")。提供只读属性firstName和lastName。主构造器参数应该是var,val还是普通参数?为什么?

val,如果为var的话,对应的字符串有get和set方法,因为只提供只读属性firstName和lastName,不能重复赋值。

8.创建一个Car类,以只读属性对应制造商,型号名称,型号年份以及一个可读写的属性用于车牌。提供四组构造器。每个构造器fc都要求制造商和型号为必填。型号年份和车牌可选,如果未填,则型号年份为-1,车牌为空串。你会选择哪一个作为你的主构造器?为什么?

class Car(val maker:String,val typeName:String){

辅助构造器参数分别为 型号年份、车牌、型号年份和车牌,不知道对不。

9.考虑如下的类

class Employ(val name:String,var salary:Double){ 
    def this(){this("John Q. Public",0.0)} 

重写该类,使用显示的字段定义,和一个缺省主构造器。你更倾向于使用哪种形式?为什么?

class Employ{
  val name:String = "John Q. Public"
  var salary:Double = 0.0
}

2

时间: 2024-10-20 05:14:38

快学Scala-第五章 类的相关文章

快学scala 第十一章 操作符 读书笔记及习题答案代码

chapter 11 操作符 标签:快学scala 一.笔记 scala种可以在反引号中包含几乎任何字符序列, val 'val' = 42 所有的操作符都是左结合的,除了以冒号(:)结尾的操作符,和赋值操作符.用于构造列表的::操作符是又结合的.1::2::Ni1的意思是1::(2::Ni1),先创建出包含2的列表,这个列表又被作为尾巴拼接到以1作为头部的列表中. 2. 函数调用语法:f(arg1, arg2,...)扩展到可以应用于函数之外的值,如果f不是函数或方法,那么这个表达式等于f.a

快学Scala第13章----集合

本章要点 所有集合都扩展自Iterable特质 集合有三大类:序列.集.映射 对于几乎所有集合类,Scala都同时提供了可变的和不可变的版本 Scala列表要么是空的,要么拥有一头一尾,其中尾部本身又是一个列表 集是无先后次序的集合 用LinkedhashSet 来保留插入顺序,或者用SortedSet来按顺序进行迭代 '+' 将元素添加到无先后次序的集合中: +: 和 :+ 向前或向后追加到序列: ++将两个集合串接在一起: -和–移除元素 Iterable和Seq特质有数十个用于常见操作的方

快学Scala第14章----模式匹配和样例类

本章要点 match表达式是一个更好的switch,不会有意外掉入到下一个分支的问题. 如果没有模式能够匹配,会抛出MatchError.可以用case _ 模式来避免. 模式可以包含一个随意定义的条件,称作守卫. 你可以对表达式的类型进行匹配:优先选择模式匹配而不是isInstanceOf/asInstanceOf. 你可以匹配数组.元组和样例类的模式,然后将匹配到的不同部分绑定到变量. 在for表达式中,不能匹配的情况会被安静的跳过. 样例类继承层级中的公共超类应该是sealed的. 用Op

快学Scala第10章----特质

本章要点 类可以实现任意数量的特质 特质可以要求实现它们的类具备特定的字段.方法或超类 和Java接口不同,Scala特质可以提供方法和字段的实现 当你将多个特质叠加在一起时,顺序很重要--其方法先被执行的特质排在更后面 为什么没有多重继承 Scala和Java一样不允许类从多个超类继承:从多了超类继承可能会导致许多问题,例如两个超类有相同的方法,子类该如何使用和菱形继承.在java 中类只能扩展自一个超类,它可以实现任意数量的接口,但接口只能包含抽象方法,不能包含字段. Scala提供了特质(

快学Scala 第6章 对象 - 练习

1. 编写一个Conversions对象,加入inchesToCentimeters.gallonsToLiters和milesToKilometers方法. object Conversions {     def main(args: Array[String]){         printf("1 inch = %g centimeters\n", inchesToCentimeters(1))         printf("2 gallons = %g liter

快学Scala 第18章 高级类型 习题解答

1. 实现一个Bug类,对沿着水平线爬行的虫子建模.move方法向当前方向移动,turn方法让虫子转身,show方法打印出当前的位置.让这些方法可以被串接调用.例如: bugsy.move(4).show().move(6).show().turn().move(5).show() 上述代码应显示 4 10 5. package ex18_01 class Bug {   var x = 0   var y = 0   var curr_direction = 0   def move(len:

快学Scala第2章–控制结构和函数 笔记

条件表达式 在Scala中,if/else 表达式是有值的,这个就是跟在if或者else之后的表达式的值.例如: val s = if(x > 0) 1 else -1 // 类似于 var s = 0 if(x > 0) s = 1 else s = -1 Scala允许使用混合类型的返回值,例如: if(x > 0) "positive" else -1 上式表达式返回的类型是它们类型的公共超类型, 在这里java.lang.String 和 Int 它们的公共超

快学Scala 第17章 - 类型参数 习题解答

1. 定义一个不可变类Pair[T,S],带一个swap方法,返回组件交换过位置的新对偶. package ex17_01 object Main extends App {   val p = new Pair(97 -> 'a')   val a = p.swap   println(a) } class Pair[T, S](val p: (T, S)) {   def swap = {     (p._2, p._1)   } } /*output: (a,97) */ 2. 定义一个可

快学Scala 第13章 集合 - 练习解答

1. 编写一个函数,给定字符串,产出一个包含所有字符的下标的映射.举例来说:indexes("Mississippi")应返回一个映射,让'M'对应集{0},'i'对应集{1,4,7,10},依此类推. 使用字符到可变集的映射.另外,你如何保证集是经过排序的? 回答:使用SortedSet可以保证集是经过排序的. package ex13_01 import scala.collection.mutable.SortedSet import scala.collection.mutab

快学Scala 第五课 (构造映射,获取映射值,更新映射值,迭代映射,与Java互操作)

构造映射: val score = Map[String, Int]() val score1 = HashMap[String, Int]() val value1 = Map[String, Int]("aa" -> 1, "bb" -> 2) val value2 = Map[String, Int](("aa", 1), ("bb", 2)) 获取映射值: println(if(value2.contain