Scala基础(二)

Scala基础(二)

单例类

一个object是一个只有一个实例的类,它在被引用的时候才创建,像一个lazy val。

作为一个顶层的值,一个object就是一个单例。

作为一个封闭类的成员或局部变量,它表现的就很像一个lazy val。

定义一个单例对象

一个object是一个值,定义一个object就像一个类,只是用object关键字

 

object Box

 这里有一个简单的只有一个方法的object。

package logging

object Logger {
  def info(message: String): Unit = println(s"INFO: $message")
}

 这个info方法可以在程序的任意地方被引入。像这样创建工具方法是单例对象的常见用例。让我们看看如何在另外的包中使用info

import logging.Logger.info

class Project(name: String, daysToComplete: Int)

class Test {
  val project1 = new Project("TPS Reports", 1)
  val project2 = new Project("Website redesign", 5)
  info("Created projects")  // Prints "INFO: Created projects"
}

 由于这个import logging.Logger.info import语句,info方法就变得可见了。

 

import需要一个稳定路径(stable path)指向被引入的符号,一个object就是一个稳定路径。

注意:如果一个object不是顶层的,而是嵌入在另一个class或object中。那么这个object就是和其他任何成员一样的路径相关(path-dependent)的。这意味着给定两种饮料,class Milk和class OrangeJuice,一个类成员object NutritionInfo 依赖于封闭实例,milk或orange juice。milk.NutritionInfo和oj.NutritionInfo是完全不同的。

伴随对象(Companion object)

和类的名字一样的对象称为伴随对象(companion object)。相反的,这个类是这个对象的伴随类(companion class)。一个伴随类或伴随对象可以访问它的同伴的私有成员。为伴随类中和特定对象无关的方法和值用一个伴随对象。

import scala.math._

case class Circle(radius: Double) {
  import Circle._
  def area: Double = {
    calculateArea(radius)
  }
}

object Circle {
  private def calculateArea(radius: Double): Double = {
    Pi * pow(radius, 2.0)
  }

  def main(args: Array[String]): Unit = {
    val circle1 = new Circle(5.0)
    println(circle1.area)
  }
}

 

类Circle有一个对于每个实例都不一样的成员area,单例object Circle有一个对于每个实例都可用的方法calculateArea。

伴随对象也可以包含工厂方法(factory method):

package tour.singleton_objects

class Email(val username: String, val domainName: String)

object Email {
  def fromString(emailString: String): Option[Email] = {
    emailString.split('@') match {
      case Array(a, b) => Some(new Email(a, b))
      case _ => None
    }
  }

  def main(args: Array[String]): Unit = {
    val scalaCenterEmail = Email.fromString("scala.center@epfl.ch")
    scalaCenterEmail match {
      case Some(email) => {
        println(
          s"""Registered an email
             |Username: ${email.username}
             |Domain name: ${email.domainName}
           """)
      }
      case None => {
        println("Error: could not parse email")
      }
    }
  }
}

 

object Email包含一个工厂方法fromString,从一个字符串创建一个Email实例。考虑到解析错误,我们返回一个Option[Email]。

注意:如果一个类或对象有一个伴随,它们必须都定义在同一个文件中。为了在REPL中定义伴随,可以在同一行定义它们,或者进入:paste模式。

 

Java中的static成员可以作为Scala中伴随对象的普通成员的模型。

当使用Java代码中的伴随对象时,成员将会在一个伴随类中用static修饰符定义。这个称谓static forwarding。即使你没有为你自己定义一个伴随类也会发生这种情况。

 

正则表达式匹配

正则表达式是可以用来在数据中查找模式的字符串。任何字符串都可以通过 .r 方法转变成正则表达式。

package tour.regex

import scala.util.matching.Regex

object RegexExample {

  def main(args: Array[String]): Unit = {
    val numberPattern: Regex = "[0-9]"r

    numberPattern.findFirstMatchIn("awesomepassword") match {
      case Some(_) => println("Password OK")
      case None => println("Password must contain a number")
    }
  }
}

 

在上面的例子中,numberPattern是一个Regex,我们用它来确保密码中包含数字。

我们也可以用小括号来搜索正则表达式的组(group)。

package tour.regex

import scala.util.matching.Regex

object RegexExample {

  def regexTest() = {
    val numberPattern: Regex = "[0-9]"r

    numberPattern.findFirstMatchIn("awesomepassword") match {
      case Some(_) => println("Password OK")
      case None => println("Password must contain a number")
    }
  }

  def regexGroupTest(): Unit = {
    val keyValuePattern: Regex = "([0-9a-zA-Z-#() ]+): ([0-9a-zA-Z-#() ]+)".r

    val input: String =
      """background-color: #A03300;
        |background-image: url(img/header100.png);
        |background-position: top center;
        |background-repeat: repeat-x;
        |background-size: 2160px 108px;
        |margin: 0;
        |height: 108px;
        |width: 100%;""".stripMargin

    for (patternMatch <- keyValuePattern.findAllMatchIn(input)) {
      println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}")

      /**
        * key: background-color value: #A03300
        * key: background-image value: url(img
        * key: background-position value: top center
        * key: background-repeat value: repeat-x
        * key: background-size value: 2160px 108px
        * key: margin value: 0
        * key: height value: 108px
        * key: width value: 100
        */
    }
  }

  def main(args: Array[String]): Unit = {
    regexTest()
    regexGroupTest()
  }
}

 

这里我们解析字符串中键值对,每个匹配有一组子匹配。

 

提取对象(Extractor Objects)

一个extractor object是一个有一个unapply方法的对象。apply方法像一个构造函数,接受参数并创建对象,但是unapply接受一个对象,然后尝试恢复参数。这个在pattern matching和部分函数(partial function)中经常用到。

 

package tour.class_related

import scala.util.Random

object CustomerID {

  def apply(name: String) = {
    println("call apply")
    s"$name--${Random.nextLong()}"
  }

  def unapply(customerID: String): Option[String] = {
    println("call unapply")
    val name = customerID.split("--").head
    if (name.nonEmpty) Some(name) else None
  }

  def main(args: Array[String]): Unit = {
    val customerID = CustomerID("Sukyoung")
    println("next call match")
    customerID match {
      case CustomerID(name) => println(name)
      case _ => println("Could not extract a CustomerID")
    }
  }
}
 

 

上面这段程序会打印:

call apply

next call match

call unapply

Sukyoung

 

apply方法从一个name创建了一个CustomerID, unapply完成了相反的动作,把name找回来了。当我们调用CustomerID("Sukyoung")时,这是调用CustomerID.apply("Sukyoung")的简写语法。当我们调用case CustomerID(name) => println(name)时,我们调用了unapply方法。

unapply方法也可以被用于赋值。

val customer2ID = CustomerID("Nico")
val CustomerID(name) = customer2ID
println(name)  // prints Nico

 

这等于val name = CustomerID.unapply(customer2ID).get,如果没有匹配的,会抛出scala.MatchError

val CustomerID(name2) = "--asdfasdfasdf"

 

unapply的返回值应该从以下选项中选择一个:

  • 如果只是一个测试,可以放回一个Boolean。例如case even()
  • 如果他们返回类型的一个次值(sub-value),返回一个Option[T]
  • 如果你想要放回多个次值T1,T2,...,Tn,用一个Option元组将它们组合起来Option[(T1,T2,...,Tn)]

有的时候,次值的数量不是固定的,我们想要返回一个列表(sequence)。为了这个原因,你也可以通过unapplySeq来定义模式,返回Option[Seq[T]]。这个机制在模式case List(x1,...,xn)的实例中使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值