一、前言

  前面学习了Scala的Methods,接着学习Scala中的Object

二、Object

  Object在Scala有两种含义,在Java中,其代表一个类的实例,而在Scala中,其还是一个关键字,本篇首先将会把object当成一个类的实例看待,展示如何将对象从一种类型转化为另一种类型,之后将展示如何创建单例对象,Scala中还存在包对象,在Scala中,经常有如下定义

type Throwable = java.lang.Throwable
type Exception = java.lang.Exception
type Error = java.lang.Error
type Seq[+A] = scala.collection.Seq[A]
val Seq = scala.collection.Seq

  使用type定义可以使得代码更为简洁,可使用伴生对象来创建静态方法,并且伴生对象可以使得在创建类对象时不需要使用new关键字,如下所示

val siblings = List(Person("Kim"), Person("Julia"), Person("Kenny"))

  2.1 对象转化

  1. 问题描述

  你需要将一个类的实例从一种类型转化为另一种类型,如动态创建对象

  2. 解决方案

  使用asInstanceOf方法进行类型转化,如下的lookup方法返回的对象将给转化为Recognizer对象 

val recognizer = cm.lookup("recognizer").asInstanceOf[Recognizer]

  以上代码在Java中如下 

Recognizer recognizer = (Recognizer)cm.lookup("recognizer");

  asInstanceOf方法是定义在Any类中的,所以任何类中都可以使用该方法

  3. 讨论

  在动态编程中,经常需要从一个类转化为另一个类,如在Spring框架中使用ApplicationContext文件来初始化Bean 

// open/read the application context file
val ctx = new ClassPathXmlApplicationContext("applicationContext.xml")
// instantiate our dog and cat objects from the application context
val dog = ctx.getBean("dog").asInstanceOf[Animal]
val cat = ctx.getBean("cat").asInstanceOf[Animal]

  在进行数字类型转化时,也可以使用asInstanceOf方法

  

  当需要与Java进行交互时,也可以使用asInstanceOf方法  

val objects = Array("a", 1)
val arrayOfObject = objects.asInstanceOf[Array[Object]]
AJavaClass.sendObjects(arrayOfObject)

  与Java类似,类型转化可能会抛出ClassCastException异常

  

  可以使用try/catch来解决此问题

  2.2 与Java的.class对应的方法

  1. 问题描述

  当一个API需要你传递Class对象,在Java中,你可以使用.class,但是在Scala则行不通

  2. 解决方案

  使用Scala的classOf方法,如下所示  

val info = new DataLine.Info(classOf[TargetDataLine], null)

  在Java中则使用如下  

info = new DataLine.Info(TargetDataLine.class, null);

  classOf方法定义在Predef对象中,因此可以在没有import情况直接使用

  3. 讨论

  该方法可以让你开始学习反射,如下示例可以访问String类的方法

  

  2.3 确定对象的类

  1. 问题描述

  在Scala中,你不需要显示的声明类型,你偶尔也想要打印一个对象的类或类型以明白Scala的工作机制

  2. 解决方案

  你可以使用对象的getClass方法来确定Scala为你自动赋值的类型,如当需要了解可变参数的工作流程时,可以调用getClass方法,得知不同情况,类型也不相同 

def printAll(numbers: Int*) {
println("class: " + numbers.getClass)
}

  当使用多个参数调用printAll方法和不使用参数调用printAll方法,其结果不相同

  

  当处理Scala的XML库时,该方法非常有效,可以知道在不同情形下所处理的类,如下,<p>标签下包含了一个子类

  

  当在<p>标签中添加<br/>标签后,其结果如下

  

  3. 讨论

  若在IDE中无法得知对象的类型,可以使用getClass方法来获取对象类型

  

  2.4 使用对象启动应用

  1. 问题描述

  你想要使用main方法来启动一个应用,或者为脚本提供一个入口

  2. 解决方案

  启动应用有两种方法,其一让类继承App,其二是定义一个对象并定义main方法

  对于第一种方法,其通用做法如下 

object Hello extends App {
println("Hello, world")
}

  此时object内的语句会自动执行,第二种方法是定义main方法

object Hello2 {
def main(args: Array[String]) {
println("Hello, world")
}
}

  3. 讨论

  上述两种方法中,都是通过object来启动应用的

  2.5 使用Object创建单例

  1. 问题描述

  你想要创建一个单例对象

  2. 解决方案

  使用object关键字来创建单例对象  

object CashRegister {
def open { println("opened") }
def close { println("closed") }
}

  由于CashRegister被定义为成object,因此仅仅只有一个实例,被调用的方法就相当于Java中的静态方法,调用如下 

object Main extends App {
CashRegister.open
CashRegister.close
}

  在创建工具方法时,该方法同样有效  

import java.util.Calendar
import java.text.SimpleDateFormat object DateUtils {
// as "Thursday, November 29"
def getCurrentDate: String = getCurrentDateTime("EEEE, MMMM d") // as "6:20 p.m."
def getCurrentTime: String = getCurrentDateTime("K:m aa") // a common function used by other date/time functions
private def getCurrentDateTime(dateTimeFormat: String): String = {
val dateFormat = new SimpleDateFormat(dateTimeFormat)
val cal = Calendar.getInstance()
dateFormat.format(cal.getTime())
}
}

  由于方法是定义在object中,因此可以直接使用DateUtils来调用这些方法,如同Java中调用静态方法 

DateUtils.getCurrentDate
DateUtils.getCurrentTime

  在使用actors时,单例对象可以产生可重用的消息,如果你有可以接受和发送消息的actor,你可以使用如下方法创建单例 

case object StartMessage
case object StopMessage

  这些对象将会被作为消息,并且可被传递到actor中  

inputValve ! StopMessage
outputValve ! StopMessage

  3. 讨论

  当使用伴生对象时,一个类就既可以有非静态方法又可以有静态方法

  2.6 使用伴生对象创建静态成员

  1. 问题描述

  你想要为一个类创建实例方法和类方法,但是Scala中没有static关键字

  2. 解决方案

  在class中定义非静态成员,在object中定义静态成员,对象与类要有相同的名字并且位于同一个文件中,该对象称为伴生对象

  使用该方法可以让你创建静态成员(字段和方法) 

// Pizza class
class Pizza (var crustType: String) {
override def toString = "Crust type is " + crustType
} // companion object
object Pizza {
val CRUST_TYPE_THIN = "thin"
val CRUST_TYPE_THICK = "thick"
def getFoo = "Foo"
}

  Pizza类和Pizza对象在同一个文件中(Pizza.scala),Pizza对象中的成员等效于Java类中的静态成员 

println(Pizza.CRUST_TYPE_THIN)
println(Pizza.getFoo)

  你也可按照常规方法创建Pizza对象

var p = new Pizza(Pizza.CRUST_TYPE_THICK)
println(p)

  3. 讨论

  class和object具有相同的名称并且在同一个文件中,class中定义的是非静态成员,object中定义的是静态成员

  class和其伴生对象可以互相访问对方的私有成员,如下面object的double方法可以访问class中的私有变量secret  

class Foo {
private val secret = 2
} object Foo {
// access the private class field 'secret'
def double(foo: Foo) = foo.secret * 2
} object Driver extends App {
val f = new Foo
println(Foo.double(f)) // prints 4
}

  如下的class类中的非静态方法可以访问伴生对象中的静态私有变量 

class Foo {
// access the private object field 'obj'
def printObj { println(s"I can see ${Foo.obj}") }
} object Foo {
private val obj = "Foo's object"
} object Driver extends App {
val f = new Foo
f.printObj
}

  2.7 将常用代码放在包对象中

  1. 问题描述

  你想要使方法、字段和其他代码处于包级别,而不需要class或者object

  2. 解决方案

  将代码放置在包对象下面,如将你的代码放置在package.scala文件中,例如,如果你想要代码被com.hust.grid.leesf.model包下所有类可用,那么创建一个位于com/hust/grid/leesf/model目录下的package.scala文件,在package.scala中,在包声明中移除model,并且以其作为名字来创建包,大致如下 

package com.hust.grid.leesf

package object model {

  其他代码放置在model中,如下所示

package com.hust.grid.leesf

package object model {
// field
val MAGIC_NUM = 42 // method
def echo(a: Any) { println(a) } // enumeration
object Margin extends Enumeration {
type Margin = Value
val TOP, BOTTOM, LEFT, RIGHT = Value
} // type definition
type MutableMap[K, V] = scala.collection.mutable.Map[K, V] val MutableMap = scala.collection.mutable.Map
}

  此时,在com.hust.grid.leesf.model包下面类、对象、接口等可以随意访问上述定义的字段、方法等

package com.hust.grid.leesf.model

object MainDriver extends App {
// access our method, constant, and enumeration
echo("Hello, world")
echo(MAGIC_NUM)
echo(Margin.LEFT)
// use our MutableMap type (scala.collection.mutable.Map)
val mm = MutableMap("name" -> "Al")
mm += ("password" -> "123")
for ((k,v) <- mm) printf("key: %s, value: %s\n", k, v)
}

  3. 讨论

  最疑惑的是将package对象放在哪里,其包名和对象名

  如果你想要让你的代码在com.hust.grid.leesf.model包中可见,那么将package.scala放在com/hust/grid/leesf/model目录下,而在package.scala中,其包名应该如下 

package com.hust.grid.leesf

  然后使用model作为对象名

package object model {

  最后大致如下  

package com.hust.grid.leesf

package object model {

  包中可以存放枚举类型、常量和隐式转换

  2.8 不使用new关键字来创建对象实例

  1. 问题描述

  当不使用new关键字来创建对象时,Scala代码会显得相对简洁,如下所示  

val a = Array(Person("John"), Person("Paul"))

  2. 解决方案

  有两种方式

    · 为类创建伴生对象,然后定义apply方法,其签名与构造方法签名相同

    · 将类定义为case类

  为Person对象定义了伴生对象,然后定义apply方法并接受参数

class Person {
var name: String = _
} object Person {
def apply(name: String): Person = {
var p = new Person
p.name = name
p
}
}

  现在你可以不使用new关键字来创建Person对象了  

val dawn = Person("Dawn")
val a = Array(Person("Dan"), Person("Elijah"))

  Scala编译器会对伴生对象中的apply进行特殊处理,让你不使用new关键字即可创建对象

  将类定义为case类,并且接受相应参数  

case class Person (var name: String)

  现在可以采用如下方法创建对象

val p = Person("Fred Flinstone")

  Scala会为case类的伴生对象创建apply方法

  3. 讨论

  编译器会对伴生对象的apply做特殊处理,这是Scala的语法糖  

val p = Person("Fred Flinstone")

  上述代码会被转化为如下代码 

val p = Person.apply("Fred Flinstone")

  apply方法是工厂方法,Scala的此语法糖让你不用new关键字即可创建对象

  可以在伴生对象中创建多个apply方法,这样相当于多个构造函数

class Person {
var name = ""
var age = 0
} object Person {
// a one-arg constructor
def apply(name: String): Person = {
var p = new Person
p.name = name
p
} // a two-arg constructor
def apply(name: String, age: Int): Person = {
var p = new Person
p.name = name
p.age = age
p
}
}

  可以使用如下方法创建对象  

val fred = Person("Fred")
val john = Person("John", 42)

  为了给case类创建多个构造函数,需要知道case类背后的逻辑

  当使用scala编译器编译case类时,你会发生其生成了两个文件,Person$.class和Person.class文件,当使用javap反编译Person$.class文件时,其输出如下

  

  其包含了一个返回Person对象的apply方法 

public Person apply(java.lang.String);

  String对应的是case类中的name  

case class Person (var name: String)

  使用javap命令可以看到在Person.class中为name生成的getter和setter函数

  

  在如下代码中,存在case类和apply方法 

// want accessor and mutator methods for the name and age fields
case class Person (var name: String, var age: Int) // define two auxiliary constructors
object Person {
def apply() = new Person("<no name>", 0)
def apply(name: String) = new Person(name, 0)
}

  由于name和age都是var的,所以会生成getter和setter,在object中定义了两个apply函数,因此可以使用如下三种方式来生成Person对象 

object Test extends App {
val a = Person()
val b = Person("Al")
val c = Person("William Shatner", 82)
println(a)
println(b)
println(c)
// test the mutator methods
a.name = "Leonard Nimoy"
a.age = 82
println(a)
}

  其结果如下  

Person(<no name>,0)
Person(Al,0)
Person(William Shatner,82)
Person(Leonard Nimoy,82)

  2.9 使用apply来实现工厂方法

  1. 问题描述

  为了让子类声明应该创建哪种类型的对象,并且只在一处能够创建对象,你想要实现工厂方法

  2. 解决方案

  可以使用伴生对象的apply方法来实现工厂方法,你可将工厂实现算法放置在apply方法中

  假设你想要创建一个Animal工厂,并且返回Cat和Dog,在Animal类的伴生对象中实现apply方法,你就可以使用如下方式创建不同对象 

val cat = Animal("cat") // creates a Cat
val dog = Animal("dog") // creates a Dog

  首先需要创建一个Animal的trait  

trait Animal {
def speak
}

  然后在相同文件中创建伴生对象,创建实现Animal的类,一个合适的apply方法 

object Animal {
private class Dog extends Animal {
override def speak { println("woof") }
} private class Cat extends Animal {
override def speak { println("meow") }
} // the factory method def apply(s: String): Animal = {
if (s == "dog") new Dog
else new Cat
}
}

  然后就可以使用如下语句创建不同对象

val cat = Animal("cat") // creates a Cat
val dog = Animal("dog") // creates a Dog

  3. 讨论

  如果不使用apply方法来实现工厂方法,也可以使用如下的getAnimal方法来实现上述功能  

// an alternative factory method (use one or the other)
def getAnimal(s: String): Animal = {
if (s == "dog") return new Dog
else return new Cat
}

  然后可以使用如下方法创建不同对象 

val cat = Animal.getAnimal("cat") // returns a Cat
val dog = Animal.getAnimal("dog") // returns a Dog

  以上两种方法都是可行的

三、总结

  本篇学习了Scala中的object及其相应的用法,其在Scala的实际编程中应用也是非常广泛,也谢谢各位园友观看~

【Scala】Scala之Object的更多相关文章

  1. scala 学习:object 和class, trait

    object: Scala中没有静态修饰符,static,在object下的成员全部都是静态的,如果在类中声明了与该类相同的名字的object则该object是该类的"半生对象", ...

  2. Scala对class/object反射

    近期有需求,要根据解析字符串,根据字符串的内容去调用方法.想到的基本就是使用反射.但是基本上能找到的资料,全没有讲scala的反射.只有零星点点的讲解.大部分都是用scala的语法写java反射最后翻 ...

  3. scala class和object,trait的区别

    Scala类 1 2 3 4 5 6 7 8 9 10 11 12 13 class Counter {   private var value = 0 // 必须初始化字段   def increm ...

  4. [Scala]Scala学习笔记五 Object

    1. 单例对象 Scala没有静态方法或静态字段,可以使用object来达到这个目的,对象定义了某个类的单个实例: object Account{ private var lastNumber = 0 ...

  5. [Scala] Scala基础知识

    Object An object is a type of class that can have no more than one instance, known in object-oriente ...

  6. [scala] scala 集合(⑧)

    1.List 基础操作 2.Set 基础操作 3. TreeSet 排序Set 4. 拉链操作 5. 流 import scala.collection.immutable.TreeSet impor ...

  7. [scala] scala 函数 (⑦)

    1.scala 函数定义 2.scala 高阶函数 3.匿名函数 4.柯里化 import scala.math._ /** * @author xwolf * @date 2017-04-24 9: ...

  8. [Scala]Scala安装以及在IDEA中配置Scala

    一  Scala简述 Scala (斯卡拉)是一门多范式(multi-paradigm)的编程语言. 这里所谓的范式,指的是编写程序的方式,不同的编程语言,方式也不尽相同,也就意味着Scala编程语言 ...

  9. [Scala]Scala学习笔记七 正则表达式

    1. Regex对象 我们可以使用scala.util.matching.Regex类使用正则表达式.要构造一个Regex对象,使用String类的r方法即可: val numPattern = &q ...

  10. [Scala]Scala学习笔记六 文件

    1. 读取行 读取文件,可以使用scala.io.Source对象的fromFile方法.如果读取所有行可以使用getLines方法: val source = Source.fromFile(&qu ...

随机推荐

  1. Angular - - $compile编译服务与指令

    $compile 这是个编译服务.编译一段HTML字符串或者DOM的模板, 产生一个将scope和模板连接到一起的函数. 编译服务主要是为指令编译DOM元素,下面的一大段也是主要介绍指令的. 下面是一 ...

  2. PHP做负载均衡回话保持问题参考

    最近一个项目的服务器老是出现Session数据丢失问题,导致用户莫名其妙的退出,原因是太相信我们的运维人员所谓的负载均衡会话保持的概念.会话保持 的原理就是负载均衡通过Cookie来分发那个客户连接被 ...

  3. 关于老版本ubuntu源不能用的问题

    在解决方向键为大写ABCD时安装vim 我的是Ubuntu 10.10 老版本 输入 sudo apt-get install vim 时出现 Package 'vim' has no install ...

  4. spring 里面的StringUtils,先放这儿,有时间研究吧

    /* * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Vers ...

  5. #图# #最大生成树# #kruskal# ----- OpenJudge 799:Heavy Transportation

    OpenJudge 799:Heavy Transportation 总时间限制: 3000ms 内存限制: 65536kB 描述BackgroundHugo Heavy is happy. Afte ...

  6. Spring IO Platform简介及示例

    什么是Spring IO Platform Spring IO Platform,简单的可以认为是一个依赖维护平台,该平台将相关依赖汇聚到一起,针对每个依赖,都提供了一个版本号: 这些版本对应的依赖都 ...

  7. Linux下使用javac编译

    Linux下使用javac编译Hadoop程序 首先要配置好Hadoop, 给出两个教程 Hadoop安装教程单机/伪分布式配置Hadoop2.6.0/Ubuntu14.04 Hadoop集群安装配置 ...

  8. python之twisted模块安装

    Twisted是一个事件驱动的网络框架. 最近开始学习了解Twisted,首先肯定要安装twisted模块. 但是在cmd下执行:pip install twisted 出现了下面的问题:" ...

  9. GIS制图课程目录

    由于更新次序跳跃式更新,因此很有必要整理一下全书目录,并将会按照实际学习的顺序进行更新. [前言] GIS制图课程前言 [理论篇] 理论篇-地图学与GIS制图的基础理论(一) 理论篇-地图学与GIS制 ...

  10. uml视频系列(二)——uml的概述

    在与uml进行了第一次的接触后,就被uml的博学多才给迷住了,uml居然可以做这么多的东西.才思敏捷的uml是设计软件的好帮手. 你还在为自己的类图不会设计而感到无助吗?你还在为你的对象不好确定而感到 ...