操作符

操作符会涉及到以下部分的内容:

1.实现自己的操作符;

2.隐式转换(自动被应用的类型转换);

3.apply、udpate和unapplay这些特殊方法。


1.中置操作符:

Scala中包含下边的表达式:

a 标识符 b

其中的标识符代表一个带有两个参数的方法(一个隐式的参数和一个显示的参数)。

例如:1 to 10 实际上是 1.to(10)。

这样的表达式叫做中置表达式,因为操作符位于两个参数之间。如果在自己的类中定义操作符,需要以你想要用作操作符的名称来定义一个方法。

下面定义的这个类,自定义了一个对象的操作符:

/**
 * 定义两个对象相乘的操作符,来实现如下公式:(n1/d1) * (n2/d2) = (n1*n1) / (d1*d2)
 * Infixtest包含2个成员num和dev, 定义乘号(*) 即:Infixtest的2个对象(infix1和infix2)相乘 , 
 * 也就是: Infix1 * Infix2 = (infix1.n1 * infix2.n2)/(infix1.d1 * infix2.d2)
 */
class Infixtest(val num:Double , val dev:Double){
  def *(other:Infixtest) = {
    (num * other.num)/(dev*other.dev)
  }
}

测试语句:

def main(args:Array[String]) {
  
val infix1 =  new Infixtest(2.1,2)
val infix2 =  new Infixtest(10.2,4)
println("两个对象的乘积:" + infix1.*(infix2))
    
}

这样我们就自定义了*(当然可以选择任何你喜欢的操作符,如#*等)实现了对象的相乘。


2.一元操作符:

只有一个参数的操作符称为一元操作符。如果操作符出现在参数后,那么就是一个后置操作符(即: a 标识符 )。相反则是前置操作符(即:标识符 a)。

后置操作符:

1 toString 等同于 1.toString()

前置操作符:

+ - ! ~ 可以作为前置操作符。它们被转换成对名为unary_操作符的方法调用。

-a  等同于  a.unary_-

println(1.unary_-)


3.优先级:

在有多个操作符同时出现,但是又没有括号时,判断操作符的优先级就十分重要了。

Scala可以随意定义操作符,因此需要用一套优先级判定方案,对所有操作符生效,但同时保留人们所熟悉的标准操作符的优先顺序。

除了赋值操作符外(a += 2 ),优先级由操作符的首字符决定。

后置操作符优先级低于中置操作符:

a 中置操作符 b 后置操作符  等同于:  (a 中置操作符 b) 后置操作符


当有一系列同优先级的操作符时,操作符的结合性决定了它们是从左到右还是从右到左求值。绝大部分操作符都是左结合的(例如,12+8+21 等同于 (12+8)+21),除了以下两种情况:

  1. 以冒号(:)结尾的操作符;

  2. 赋值操作符


4.apply和update方法:

函数或者方法可以如下边方式调用:

f(arg1,arg2,...)

如果f不会函数或者方法,那么这个表达式就等同于调用apply方法:

f.apply(arg1,arg2,...)

但是如果它出现在赋值语句的等号的左侧,即:f(arg1,arg2,...) = value,则等同于调用update方法:

f.update(arg1,arg2,...)

下面是一个使用apply和update方法的实例:

/**
 * apply方法和update方法:
*/
import scala.collection.mutable.HashMap
val hashtest = new HashMap[String,Int]
//f(arg1,arg2,...)在赋值语句等号的左侧,相当于调用 f.update。 
//下边语句相当于  hashtest.update("Bob",131) 
hashtest("Bob") = 131
//相当于hashtest.apply("Bob")
val bobhash = hashtest("Bob")

apply方法也经常被用在伴生对象,用来构造对象而不用显式地使用new。


5.提取器:

所谓提取器就是一个带有unapply方法的对象。unapply方法是伴生对象中apply方法的反向操作。

apply方法接收构造参数,然后将其变为对象。而unapply方法接收一个对象,然后从中提取值——通常这些值就是当初用来构造该对象的值。

通常而言,模式匹配可能会失败。因此unapply方法返回的是一个option。它包含一个元组,每个匹配到的变量各有一个值与之对应。

下面使用上边的Infixtest类的例子,首先定义伴生对象(需要定义unapply方法):

/**
 * 定义两个对象相乘的操作符,来实现如下公式:(n1/d1) * (n2/d2) = (n1*n1) / (d1*d2)
 * Infixtest包含2个成员num和dev, 定义乘号(*) 即:Infixtest的2个对象(infix1和infix2)相乘 , 
 * 也就是: Infix1 * Infix2 = (infix1.n1 * infix2.n2)/(infix1.d1 * infix2.d2)
 */
class Infixtest(val num:Double , val dev:Double){
  def *(other:Infixtest):Infixtest = {
    Infixtest(num * other.num,dev*other.dev)
  }
}

//apply伴生对象
object Infixtest{
  //apply方法接收2个参数来创建对象。 可以省去原来根据类创建对象使用new关键字。
  def apply(num:Double,dev:Double) = new Infixtest(num,dev)
  //unapply方法接收一个对象,获得后从中提取值————通常这些值就是当初用来构造该对象的值
  def unapply(other:Infixtest) = {
    if(other.dev == 0)
        None
    else
       Some((other.num,other.dev))
  }
}

提取器的定义:

var Infixtest(a:Double,b:Double) = Infixtest(134.2,3) * Infixtest(2,12)
println("Unapply方法提取器:"+ a + " " + b)

下面,使用提取器来获取一个姓名的姓和名字符串:

/**
 * 实现unapply提取器,使用提取器来实现简单功能,给一个姓名,分别提取出姓和名
 * 定义unapply方法
 */
object Name{
  def unapply(other:String) ={
    val names = other.split(" ")
    if (names.length != 2 ) None
    else Some( (names(0),names(1)) )
  }
}

测试类:

  /**
   * 测试分拆姓名的unapply方法
  */
  val test_name = "Tom White"
  var Name(c,d) = test_name
  println("名是:" + c +" 姓是:" +d)

上边例子,没有定义Name类。Name对象是针对String对象的提取器

另外,每一个样例类都自动具备apply和unapply方法。例如:

case class Currency(value:Double,unit:String)


6.带单个参数或无参数的提取器:

如果unapply方法要提取单值,则它应该返回一个目标类型的Option。提取器也可以只是测试其输入而并不真的将值提取出来。

/**
 * 提取器提取一个或者不返回值
 */
//将数字转化为Int
object Number{
  def unapply(num:String) = {
    Some(Integer.parseInt(num))
  }
}
//确定字符串是否含空格
object IsCompond{
   def unapply(num:String) = {
    Some(num.contains(" "))
  }
}

测试语句:

  /**
   * 测试 :提取器提取一个或者不返回值
   */
  var Number(sf) = "332"
  var IsCompond(s) = "new York"
  println("数字解析:" + sf + ";Boolean解析:" + s)


7.unapplySeq方法:

要提取任意长度的序列,应该使用unapplySeq来命名方法。返回一个Option[Seq[A]],A是被提取的值的类型。

/**
 * 提取器提取一系列名字。
 */
object Names{
  def unapplySeq(args:String):Option[Seq[String]] = {
    //一个或多个空格分隔
    if (args.split("\\s+").length > 1) { Some(args.split("\\s+"))}
    else None
    }
  }

测试:

  /**
   * 测试unapplySeq方法
   */
  var Names(a1,a2,a3,a5,_,_) = "Let us go to park today!"
  println (a1+"||"+a2+"||"+a3+"||"+a5+"||")

需要注意的是,后边拆分出来的元组,必须跟前边Names()里的变量个数一致,如果不需要的话用占位符号。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章