快学Scala第5章--类

2024-05-06 07:58
文章标签 scala 快学

本文主要是介绍快学Scala第5章--类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

快学Scala第5章—–类

本章要点

  • 类中的字段自动带有getter方法和setter方法
  • 你可以用定制的getter/setter方法替换掉字段的定义,而不必修改使用类的客户端—–这就是所谓的统一访问原则
  • 用@BeanProperty注解来生成JavaBeans的getXxx/setXxx方法。
  • 每个类都有一个主要的构造器,这个构造器和类定义交织在一起。它的参数直接成为类的字段。主构造器执行类体中所有的语句。
  • 辅助构造器是可选的,它们叫做this

简单类和无参方法

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

在Scala中,类并不声明为public。Scala源文件可以包含多个类,所有这些类都具有可见性,这与C++一样,与Java有很大的不同。
使用该类需要做的就是构造该类的对象,并按照通常的方法来调用方法:

val myCounter = new Counter  // 或 new Counter()
myCounter.increment()
println(myCounter.current)  // 调用无参方法时,可以写上括号,也可以不写

对于无参方法写不写括号,一般是对于修改对象属性的方法使用圆括号,对于获得属性值的方法去掉圆括号。 也可以通过不带()的方式声明current来强制不带括号的调用:

class Counter {...def current = value   // 定义中不带()
}

带getter 和 setter的属性

Scala对每个字段都提供了getter和setter方法。在这里定义一个公有字段:

class Person {var age = 0
}

Scala生成面向JVM的类,其中有一个私有的age字段以及相应的getter和setter方法。这与age字段前面加上private是不同的,没有private修饰,age的getter和setter方法则是公有的;但是声明为private后,age的getter和setter方法则是私有的,因为对于私有字段而言,getter和setter方法也是私有的。 在Scala中,getter和setter方法分别叫做age和age_=,例如:

println(fred.age)  // 将调用 fred.age()
fred.age = 21  // 将调用fred.age=(21)

通过javap查看字节码:

$ scalac Person.scala
$ javap -privete Person
Compiled from "Person.scala"
public class Person extends java.lang.Object implements scala.ScalaObject {private int age;public int age();public void age_$eq(int);public Person();
}

编译器会自动创建age和age_ eq= eq,是因为JVM中不允许在方法名中出现=。)
在任何时候,你都可以自己重新定义getter和setter方法:

class Person {
private var privateAge = 0  // 变成私有
def age = privateAge
def age_=(newValue: Int) {
if (newValue > privateAge) private = newAge;

统一访问原则:某个模块提供的所有服务都应该能通过统一的表示方法访问到,至于它们是通过存储还是通过计算来实现,从访问方式上应无从获知。 例如 fred.age的调用者并不知道age是通过字段还是通过方法来实现的。

提示:

  • 如果字段是私有的,个getter和setter方法也是私有的,则该字段只能在类内部使用
  • 如果字段是val,则只会生成getter方法
  • 如果你不需要任何getter和setter方法,可以将字段声明为private[this]

只带getter的属性

如果属性的值在对象的构建初始化后不再改变,则可以使用val字段:

class Message {val timeStamp = new java.util.Date...
}

Scala会生成一个私有的final字段和一个getter方法,但是没有setter方法。
总结一下: 在实现属性时,你有以下四个选择
1. var foo: Scala会自动合成一个getter和一个setter;
2. val foo: Scala自动合成一个getter;
3. 有你自定义foo和foo_=方法
4. 由你来定义foo方法
在Scala中,你不能实现只写属性。


对象私有字段

在Scala中,方法可以访问该类的所有对象的私有字段。

class Counter {
private var value = 0
def increment() { value += 1 }
def isLess(other: Counter) = value < other.value  // 访问另一个对象的私有字段

但是更严格的访问限制会限制访问其他对象的私有字段:

private[this] var value = 0

这样,Counter类的方法只能访问到当前对象的value字段,而不能访问参数是Counter类型的其他对象的该字段。这样的访问被称为对象私有的,Scala不会生成的该字段的getter和setter方法。
说明: Scala允许你将访问权赋予指定的类型(有些类似C++的友元,可以访问其他类对象的私有属性)。private[类名]修饰符可以定义仅有指定类的方法可以访问给定的字段。这里的类名必须是当前定义的类,或者是包含该类的外部类。 在这种情况下,编译器会生成辅助的getter和setter方法,允许外部类访问该字段。


Bean属性

JavaBean规范把类属性定义为一对getFoo/setFoo方法。当你将Scala字段标注为@BeanProoerty时,这样的方法会自动生成:

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


辅助构造器

和C++、Java一样,Scala也可以有任意多的构造器。Scala将构造器分为了主构造器和辅助构造器,在这里先谈辅助构造器。辅助构造器与C++或Java 有两处不同:
1. 辅助构造器的名称为this
2. 每个辅助构造器都必须以一个对先前已经定义的其他辅助构造器或者主构造器的调用开始。所以主构造器是所有辅助构造器的根源。

class Person {private var name = ""private var age = 0def 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", 23)

主构造器

在Scala中,每个类都有一个主构造器。主构造器并不以this方法定义,而是与类定义交织在一起。
1. 主构造器的参数直接放置在类名之后:

class Person(val name: String, val age: Int) {// name和age就是主构造器的参数...
}

主构造器的参数被编译成字段,其值被初始化成构造时传入的参数。
2. 主构造器会执行类定义中的所有语句。

class Person(val name: String, val age: Int) {println("Just constructed another person")def description = name + " : " + age
}

说明: 如果类名之后没有参数,则该类具有一个默认的无参主构造器。
提示:可以通过在主构造器中使用默认参数来避免过多的使用辅助构造器,例如

clss Person(val name: String = "", val age: Int = 0)

主构造器的参数和前面讲的一样,可以使用val/var 和 private等修饰。
构造参数也可以是普通的方法参数,不带val/var。这样的参数如何处理,取决于它们在类中如何被使用。

  • 如果不带val或者var的参数至少被一个方法所使用,它将被升格为字段:
class Person(name: String, age: Int) {def description = name + ":" + age
}

这时,name和age就变成了类的不可变字段,而且这两个字段是对象私有的,这类似于 private[this] val 字段的效果。

  • 否则, 该参数将不被保存为字段。它仅仅是一个可以被主构造器中的代码访问的普通参数。
主构造器参数生成的字段/方法
name: String对象私有字段。如果没有方法使用name, 则没有该字段
private val/var name: String私有字段,私有的getter和setter方法
val/var name: String私有字段,公有的getter和setter放
@BeanProperty val/var name: String私有字段,公有的Scala版和Java版的getter和setter方法

如果想让主构造器变成私有的,可以在()之前加上private,这样用户只能通过辅助构造器来构造Person对象了

class Person private(val name: String, val age: Int) {...}

嵌套类

在Scala中,你可以在函数中定义函数,在类中定义类

import scala.collection.mutable.ArrayBuffer
class NetWork {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 += mm}
}// 考虑有如下两个网络
val chatter = new NetWork
val myFace = new NetWork

在Scala中,每个实例都有它自己的Member类,这就和每个实例有自己的members样,这不是共享的,因此,chatter.Member 和 myFace.Member 是不同的两个类。

val fred = chatter.join("Fred")
val wilma = chatter.join("Wilma")
fred.contacts += wilma // ok
val barney = myFace.join("Barney")
fred.contacts += barney  // error,不能将myFace.Member添加到chatter.Member元素缓冲当中

如果你不想有这个效果,可以有两种解决方式:

  • 将Member类移到别处,例如伴生对象中。
object Network {class Member(val name: String) {val contacts = new ArrayBuffer[Member]}
}class NetWork {private val members = new ArrayBuffer[Member]def join(name: String) = {val m = new Member(name)members += mm}
}
  • 另一种方式,使用类型投影 Network#Member,其含义是 任何Network的Member
class NetWork {class Member(val name: String) {val contacts = new ArrayBuffer[NetWork#Member]}private val members = new ArrayBuffer[Member]def join(name: String) = {val m = new Member(name)members += mm}
}

说明: 在内嵌类中,可以通过外部类.this的方式来访问外部类的this引用,也可以使用如下形式建立一个指向该引用的别名:

class Nerwork(val name: String) { outer =>class Member(val name: String) {...def description = name + " inside " + outher.name}
}

class Network { outer => 语法是的outer变量指向了Network.this。

这篇关于快学Scala第5章--类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/963827

相关文章

【scala 安装错误】错误: 找不到或无法加载主类 scala.tools.nsc.MainGenericRunner

错误: 找不到或无法加载主类 scala.tools.nsc.MainGenericRunner 原因: Scala安装路径中包含空格。 解决办法:scala 不要安装在E:\Program Files 这种有空格的目录下,简直坑

Scala模式匹配下提取器构造

示例代码: object :> {def unapply[A] (list:List[A]) = {Some( (list.init,list.last) )}}object Extractor_Advanced {def main(args: Array[String]): Unit = {(1 to 9).toList match{ case _ :> 9 => println(

从spark源码的角度思考scala中的模式匹配

1.scala中模式匹配 2.spark源码中的模式匹配思考 spark中master会收到worker发过来的akka的消息, 此消息是case class即(Master.class中): case class RegisterWorker(id:String,host:String,port:Int,cores:Int,memory:Int,webUiPort:int

Scala界面事件处理

示例代码: import scala.swing.SimpleSwingApplicationimport scala.swing.MainFrameimport scala.swing.Buttonimport scala.swing.Labelimport scala.swing.Orientationimport scala.swing.BoxPanelimpo

Scala界面Panel、Layout初探

示例代码: package com.dt.scala.guiimport scala.swing.SimpleSwingApplicationimport scala.swing.MainFrameimport scala.swing.Buttonimport scala.swing.Labelimport scala.swing.Orientationimport scal

scala界面GUI编程实战初步了解

示例代码: import scala.swing._//SimpleSwingApplication继承自SwingApplication类(此类中有main方法,因此可以运行显示界面)object Hello_GUI extends SimpleSwingApplication {def top = new MainFrame{ //顶级容器title = "Hello GUI"co

Scala并发编程react、loop代码实战详解

示例代码及注释: //scala并发编程中的react和loop,共同特点://通过线程存用的方式让性能有所提升。//Actor本身的运行,被actor子系统管理的时候,会有一个或者多个远程的线程让当前的actor使用//一般情况下每个Actor都有自己的线程。只有有自己的线程时,我们的Actor中的actor方法才会执行。//但是,这样线程的开销会非常大,所以为了共用线

scala并发编程原生线程Actor、Case Class下的消息传递和偏函数实战

参考代码: import scala.actors._case class Person(name:String,age:Int)class HelloActor extends Actor{def act(){while(true){receive{case Person(name,age)=>{ //偏函数println("Name: "+ name + ":" +"Age:"

scala基础概念

Scala是面向行的语言,Scala 语句末尾的分号写或者不写都可以。 对象 - 对象有属性和行为。例如:一只哈士奇的属性有:颜色,名字,行为有:叫、跑、吃等。对象是一个类的实例。 类 - 类是对象的抽象;对象是类的具体实例。 方法 - 方法描述的基本的行为,一个类可以包含多个方法。 字段 - 每个对象都有它唯一的实例变量集合,即字段。对象的属性通过给字段赋值来创建。 基本语法

Scala:Scala基础语法【Scala语言是一个完全面向对象编程语言-->万物皆对象;Scala语言是一个完全函数式编程语言-->万物皆函数】

一、变量和数据类型 1、变量 说明:在Scala中声明一个变量时,可以不指定类型,编译器根据值确定 var | val 变量名 [: 变量类型] = 变量值 声明变量时,类型可以省略(编译器自动推导,即类型推导)类型确定后,就不能修改,说明Scala是强数据类型语言。变量声明时,需要初始值object TestVar {def main(args: Array[String]): Uni