scala 学习笔记(04) OOP(上)主从构造器/私有属性/伴生对象(单例静态类)/apply方法/嵌套类
一、主从构造器
java中构造函数没有主、从之分,只有构造器重载,但在scala中,每个类都有一个主构造器,在定义class时,如果啥也没写,默认有一个xxx()的主构造器
class Person {
var name: String = _
/**
* 从构造器
* @param name
*/
def this(name: String) = {
this //注意:从构造器,必须先调用主构造器
this.name = name;
}
override def toString = {
"name:" + name
}
}
上面的这段代码,如果将生成的class反编译成java来看的话,可能更容易理解:
public class Person
{
private String name; public String name()
{
return this.name; }
public void name_$eq(String x$1) { this.name = x$1; } public String toString()
{
return new StringBuilder().append("name:").append(name()).toString();
} public Person()
{
} public Person(String name)
{
this();
name_$eq(name);
}
}
从反编译结果看,生成了二个构造函数,一个是默认的Person(),另一个是Person(String name)。
Scala是一个崇尚简约之美的语言,在定义Class时,可以将属性声明、带参构造器,一并全解决了,所以刚才这段代码,"等效"于下面这样:
class Person(var name: String) {
override def toString = {
"name:" + name
}
}
注意第一行,此时的主构造器不再是默认的无参构造器,而是Person(var name:String),它有二层意思:一是定义了一个带参数的构造器,二是由于name前加了var,说明name:String不仅仅是构造器的参数,还是类Person的一个属性成员,不过这个版本与第一个版本还是有些差别的,如果用JD-GUI反编译查看的话,会发现默认的无参构造器消失了
public class Person
{
private String name; public String name()
{
return this.name; }
public void name_$eq(String x$1) { this.name = x$1; } public String toString() {
return new StringBuilder().append("name:").append(name()).toString();
} public Person(String name)
{
}
}
Person的使用示例如下:
object App {
def main(args: Array[String]) {
val p: Person = new Person("jimmy")
println(p.toString)
p.name = "jimmy.yang"
println(p.toString)
}
}
主构造器上,还可以增加限定词private来修饰,比如下面这样:
class Person private(var name: String) {
var age: Int = 0;
def this(age: Int, name: String) = {
this(name)
this.age = age;
}
override def toString = {
"name:" + name + ",age:" + age
}
}
这样Person类的使用方,就只能使用从属构造器this(age:Int,name:String)了。
二、私有属性(private property)
将前面的Person改一下,将年龄Age设置成私有成员
package yjmyzz
class Person private(var name: String) {
println("这是主构造器的可执行语句,我是" + name) //这一行在new Person时会执行
/**
* 定义一个私有成员
*/
private var _age: Int = 0;
def age = _age
def this(age: Int, name: String) = {
this(name)
this._age = age;
}
def isOlder(another: Person) = {
this._age > another._age //注意:这里可直接访问另一个Person的私有成员_age
}
override def toString = {
"name:" + name + ",age:" + age
}
}
注意:isOlder方法,该方法用于比较二个Person谁更年长,跟java不同的是,在Class定义范围内,可以直接访问另一个类实例的私有成员!这在java、c#中是绝对不允许的。
另外,还有一个值得注意的地方,Class的定义里,除了def定义的方法(或许称为函数更适合)外,任何可执行语句都会被执行,比如第6行的println语句。下面是调用示例:
val jimmy: Person = new Person(18, "jimmy")
val mike: Person = new Person(30, "mike")
if (mike.isOlder(jimmy))
println(mike.name + " is older than " + jimmy.name)
执行结果:
这是主构造器的可执行语句,我是jimmy
这是主构造器的可执行语句,我是mike
mike is older than jimmy
如果不希望private成员在Class定义中直接被其它实例所访问,可以改成private[this],即:
private[this] var _age: Int = 0;
def age = _age
def isOlder(another: Person) = {
this._age > another.age
}
这样的话,isOlder中的another,只能通过函数age来访问私有成员_age了。
三、static成员/伴生对象Object/apply方法
scala里并没有static关键字,要达到类似的效果,可以借助object对象,object天然是singleton模式,比如下面的代码:
object Singleton {
var count = 0;
def increment: Unit = {
count += 1
}
}
定义成object类型的对象,没有办法直接new, object中的所有方法都是静态方法,这一点类似c#中的static静态类,使用时直接按静态方法调用即可:
var obj1 = Singleton.count
println(obj1)
Singleton.increment
var obj2 = Singleton.count
println(obj2)
object不仅仅用于单例模式的实现,更多时候,我们可以定义一个与class同名的object,然后把class的所有静态方法放到object里,比如:
class People(var name: String) {
println("main constructor in Class People")
}
object People {
def whoami: Unit = {
println("I am a people ")
}
def apply(name: String) = {
println("apply in Object People")
new People(name)
}
}
后面的object People称为class People的伴生对象,可以理解为class People的静态成员/方法集合,注意里面的apply方法,这个方法会被自动调用,通常用于创建对象实例,有点工厂模式的意味,看下面的调用代码:
var p:People = People("jimmy")
println(p.name)
People.whoami
这里我们没有用new关键字来创建对象,而是"隐式"调用了伴生对象的静态方式apply,以下是输出结果:
apply in Object People
main constructor in Class People
jimmy
I am a people
伴生对象+apply方法,这是scala中经常使用的一个技巧,即简化了代码,又起了工厂模式的作用,我们甚至还可以在apply方法中加入对象控制的额外业务逻辑,这比直接new对象更灵活。
从object的使用上,还可以看出静态方法的调用上scala与java的不同,java中静态方法即可以用"类名.静态方法()",也可以用"对象实例.静态方法()"来调用,说实话,有点不太讲究,而Scala"纠正"了这一错误,静态方法只能在object(即:静态类)上调用,非静态方法只能在对象实例上调用,这与c#的理念是一致的(见:java学习:OOP入门 第7点)
apply方法不仅可以存在于object中,class中也可以有apply方法,我们把People的Class改一下:
class People(var name: String) {
println("main constructor in Class People")
def apply(): Unit ={
println("apply in Class People")
}
}
然后这么调用:
var p:People = People("jimmy")
p()
注意第2行,就是这么简单!输出结果:
apply in Object People
main constructor in Class People
apply in Class People
四、内部类(也称嵌套类)
class内部还可以再定义类,即嵌套类,与java不同的是,scala的嵌套类是属于实例的,而不属于定义它的外部类。这句话听着很绕,还是直接看代码吧,先把前面的People类改造一下:
class People(var name: String) {
/**
* 定义嵌套类(注:必须写在最开始,好象也只能定义一个?)
*/
master =>
//这个master变量即指People本身this,名字可以随便取
class Pet(var name: String) {
def hi() = println("我叫" + this.name + " , 我是" + master.name + "的宠物!")
}
/**
* 增加了一个宠物属性
*/
var pet: Pet = _
}
object People {
def apply(name: String, petName: String) = {
println("apply in Object People")
val people = new People(name)
people.pet = new people.Pet(petName)
people
}
}
然后使用:
val jimmy = new People("jimmy")
val dog = new jimmy.Pet("wang wang") //注:这是调用的"实例"上的Pet,而不是new People.Pet()
dog.hi()
println("------------")
val mike = People("Mike","miao miao")
mike.pet.hi()
println("------------")
//println("jimmy与mike交换宠物:")
//jimmy.pet = mike.pet //直接报错,因为mike的宠物是只属于mike的,它与jimmy的宠物类型不兼容
//jimmy又养了一只猫
var cat = new jimmy.Pet("miao")
//然后把狗狗扔了
jimmy.pet = cat;
jimmy.pet.hi()
注意第2行及第13行,第2行是直接用 new 实例.内部类()的方式创建的,而非 new 外部类.内部类()这种方式,说明内部类是从属于外部类的实例,第13行再次证明了这一点,虽然都是内部类Pet的实例,但当试图将mike的Pet实例赋值给jimmy的Pet实例时,编译器直接报错,说明内部类的实例一旦创建,则"生是X家人,死是X家鬼",绝对的忠贞不二。
scala 学习笔记(04) OOP(上)主从构造器/私有属性/伴生对象(单例静态类)/apply方法/嵌套类的更多相关文章
- Effective Java - 构造器私有、枚举和单例
目录 饿汉式单例 静态常量 静态代码块 懒汉式单例 尝试加锁 同步代码块 双重检查 静态内部类单例 枚举单例 Singleton 是指仅仅被实例化一次的类.Singleton代表了无状态的对象像是方法 ...
- scala 学习笔记(06) OOP(下)多重继承 及 AOP
一.多继承 上篇trait中,已经看到了其用法十分灵活,可以借此实现类似"多重继承"的效果,语法格式为: class/trait A extends B with C with D ...
- scala 学习笔记(05) OOP(中)灵活的trait
trait -- 不仅仅只是接口! 接上回继续,scala是一个非常有想法的语言,从接口的设计上就可以发现它的与众不同.scala中与java的接口最接近的概念是trait,见下面的代码: packa ...
- Angular学习笔记 ——input 标签上的【name属性】和【ngModelOptions属性】
利用“@angular/forms" 创建<form>表单的时候,系统默认会创建一个”FormGroup"的对象. 使用带有“ngModel"的”<in ...
- 【大数据】Scala学习笔记
第 1 章 scala的概述1 1.1 学习sdala的原因 1 1.2 Scala语言诞生小故事 1 1.3 Scala 和 Java 以及 jvm 的关系分析图 2 1.4 Scala语言的特点 ...
- 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性
基于.net的分布式系统限流组件 在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...
- Redis:学习笔记-04
Redis:学习笔记-04 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 10. Redis主从复制 1 ...
- Scala学习笔记及与Java不同之处总结-从Java开发者角度
Scala与Java具有很多相似之处,但又有很多不同.这里主要从一个Java开发者的角度,总结在使用Scala的过程中所面临的一些思维转变. 这里仅仅是总结了部分两种语言在开发过程中的不同,以后会陆续 ...
- SaToken学习笔记-04
SaToken学习笔记-04 如果有问题,请点击:传送门 角色认证 在sa-token中,角色和权限可以独立验证 // 当前账号是否含有指定角色标识, 返回true或false StpUtil.has ...
随机推荐
- 软件光栅化渲染器Augustus计划
在看完Real-Time Rendering后,我决定动手实现一个软件的光栅化渲染器.我就称它为Augustus计划吧. 计划使用MFC和GDI+来做它的UI.可以访问GitHub来查看它的源代码.
- nodejs API
1.querystring参数处理 序列化 > querystring.stringify({'name':'scott',course:['jade','node'],from:''}) 'n ...
- Linux Buffer I/O error on device dm-4, logical block
Linux服务器日志(Oracle Linux Server release 5.7)里面出现了一些"Buffer I/O error on device dm-4, logical blo ...
- [20140829]spinlock导致cpu居高不下
背景: 出现cpu高于常规的告警 排查: 1.开跟踪,没有发现cup特别高的查询 2.查看内核cpu使用量,看是否是sql server 端引起 3.查看负荷,是否负荷特别高这里使用 batch re ...
- MySQL数据库的导入和导出
1.导入数据库 在命令行下输入: mysql -u username -p test < /home/data/test.sql 说明: username 是 ...
- SQL Server 2012 学习笔记5
1. 索引(Index) 索引是快速的定位和查找数据.索引分为: 聚集索引:唯一,默认主键,一般选取比较连贯的字段,聚集索引是物理排序. 非聚集索引: 并没有把数据物理排序,只是多了一个索引页(包括索 ...
- mysql字符编码集(乱码)问题解决
1.创建数据库 CREATE DATABASE `test` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'; 创建表 CREATE TABLE tp_w ...
- andriod 动态设置TextView 和 RelativeLayou 高度
XML布局 <RelativeLayout android:id="@+id/rlay_meeting_contact_context" android:layout_wid ...
- linux 文件系统解析及相关命令
简介 文件系统就是分区或磁盘上的所有文件的逻辑集合. 文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件.目录.软连接及文件保护信息等都存储在其中. 不同Lin ...
- 使用jlink直接烧norflash或者nandflash不借助uboot的猜想
由于喜欢折腾,我是在linux下使用jlink的,既然JLinkExe可以进行内存读写操作,loadbin等操作,并且通过指定命令文件支持批量指令输入,那么首先jlink是可以直接访问内部存储器的,包 ...