真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Scala中Trait有什么作用

這篇文章主要介紹“Scala中Trait有什么作用”,在日常操作中,相信很多人在Scala中Trait有什么作用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Scala中Trait有什么作用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

網(wǎng)站建設哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設計、網(wǎng)站建設、微信開發(fā)、小程序設計、集團企業(yè)網(wǎng)站建設等服務項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了東城免費建站歡迎大家使用!

Inside Scala - 1:Partially applied functions

Partially applied function(不完全應用的函數(shù))是scala中的一種curry機制,本文將通過一個簡單的實例來描述在scala中 partially applied function的內(nèi)部機制。

// Test3.scala  package test   object Test3 {     def sum(x:Int, y:Int, z:Int) = x + y + z        def main(args: Array[String]) {      val sum1 = sum _      val sum2 = sum(1, _:Int, 3)      println(sum1(1,2,3))      println(sum2(2))      List(1,2,3,4).foreach(println);      List(1,2,3,4).foreach(println _)    }   }

在這個代碼中 sum _ 表示了一個 新的類型為 (Int,Int,Int)=>Int 的函數(shù),實際上,Scala 會生成一個新的匿名函數(shù)(是一個函數(shù)對象,F(xiàn)unction3),這個函數(shù)對象的apply方法會調(diào)用 sum 這個對象方法(在這里,是方法,而不是一個函數(shù))。
sum2 是一個 Int => Int的函數(shù)(對象),這個函數(shù)的apply方法會調(diào)用 sum 對象方法。
后面的兩行代碼都需要訪問 println, println是在在Predef對象中定義的方法,在scala中,實際上都會生成一個臨時的函數(shù)對象,來包裝對 println 方法的調(diào)用。如果研究一下scala生成的代碼,那么可以發(fā)現(xiàn),目前生成的代碼中, 對 println, println _生成的代碼是重復的,這也說明,目前,所有的你匿名函數(shù)基本上沒有進行重復性檢查。(這可能導致編譯生成的的類更大)。

從這里可以得知,雖然,在語法層面,方法(所有的def出來的東西)與函數(shù)看起來是一致的,但實際上,二者在底層有區(qū)別,方法仍然是不可以直接定位、傳值的,他不是一個對象。而僅僅是JVM底層可訪問的一個實體。而函數(shù)則是虛擬機層面的一個對象。任何從方法到函數(shù)的轉(zhuǎn)換,Scala會自動生成一個匿名的函數(shù)對象,來進行相應的轉(zhuǎn)換。

所以, List(1,2,3,4).foreach(println) 在底層執(zhí)行時,并不是獲得了一個println的引用(實際上,根本不存在println這個可訪問的對象),而是scala自動產(chǎn)生一個匿名的函數(shù),這個函數(shù)會調(diào)用println。

當然,將一個函數(shù)傳遞時,Scala是不會再做不必要的包裝的,而是直接傳遞這個函數(shù)對象了。

Inside Scala - 2: Curry Functions

Curry,在函數(shù)式語言中是很常見的,在scala中,對其有特別的支持。

package test

object TestCurry {     def sum(x:Int)(y:Int)(z:Int) = x + y + z     def main(args: Array[String]){       val sum1: (Int => Int => Int) = sum(1)      val sum12: Int => Int = sum(1)(2)      val sum123 = sum(1)(2)(3)      println(sum1(2)(3))      println(sum12(3))      println(sum123)     }   }

在這個例子中, sum 被設計成為一個curried函數(shù),(多級函數(shù)?),研究一個函數(shù)的實現(xiàn)是很有意思的:

如果看生成的 sum 函數(shù)代碼,那么,它與 如下編寫的
def sum(x:Int, y:Int: z:Int) = x + y + z 是一致的。
而且,如果,你調(diào)用sum(1)(2)(3),實際上,scala也并不會產(chǎn)生3次函數(shù)調(diào)用,而是一次 sum(1,2,3)

也就是說,如果你沒有進行 sum(1), sum(1)(2)等調(diào)用,那么實際上,上述的代碼中根本不會生成額外的函數(shù)處理代碼。但是,如果我們需要進行一些常用的curry操作時,scala為我們提供了額外的語法級的便利。

Inside Scala - 3: How Trait works

Scala中Trait應該是一個非常強大,但又有些復雜的概念,至少與我,我對trait總是有一些不太明了的地方,求人不如求己,對這些疑問還是自己動手探真的比較好。

還是從一個簡單的實例著手。

package test

import java.awt.Point   object TestTrait {     trait Rectangular {      def topLeft: Point      def bottomRight: Point            def left = topLeft.x      def top = topLeft.y      def right = bottomRight.x      def bottom = bottomRight.y      def width = right - left      def height = bottom - top    }        class Rectangle(val topLeft: Point, val bottomRight: Point) extends Rectangular {      override def toString = "I am a rectangle"   }   }

對這段代碼,我想問如下的幾個問題:
Rectangle是如何繼承 Rectangular的行為,如 left, right, width, height的?
Rectangular 對應于Java的接口,那么,相關的實現(xiàn)代碼又是如何保存的?
其實,這兩個問題是相關的。研究這個問題的最直接的辦法莫過于直接分析scalac編譯后的結果。
這個類編譯后包括:
TestTrait.class 這個類
TestTrait$.class 其實就是 object TestTrait這個對象的類。一個object實際上從屬于一個類,scala是對其加后綴$
在這個例子中,TestTrait這個對象實際上并未定義新的屬性和方法,因此,并沒有包含什么內(nèi)容
TestTrait$Rectangular.class
對應于代碼中的Rectangular這個trait,這實際上是一個接口類。對應的就是這個trait中定義的全部方法。包括topLeft, bottomRight以及后續(xù)的實現(xiàn)方法left, width等的接口定義

public interface test.TestTrait$Rectangular extends scala.ScalaObject{   public abstract int height();  public abstract int width();  public abstract int bottom();  public abstract int right();  public abstract int top();  public abstract int left();  public abstract java.awt.Point bottomRight();  public abstract java.awt.Point topLeft();   }

TestTrait$Rectangular$class.class
這個類實際上是trait邏輯的實現(xiàn)類。由于JVM中,接口是不支持任何的實現(xiàn)代碼的,因此,scala將相關的邏輯代碼編譯在這個類中

public abstract class test.TestTrait$Rectangular$class extends java.lang.Object{   public static void $init$(test.TestTrait$Rectangular); // 在這個例子中,沒有trait的初始化相關操作    Code:     0:   return  public static int height(test.TestTrait$Rectangular);   // 對應于height = bottom - top這個操作的實現(xiàn)    Code:     0:   aload_0     1:   invokeinterface #17,  1; //InterfaceMethod test/TestTrait$Rectangular.bottom:()I     6:   aload_0     7:   invokeinterface #20,  1; //InterfaceMethod test/TestTrait$Rectangular.top:()I     12:  isub     13:  ireturn

更多的方法并不在此羅列。
首先,這個實現(xiàn)類是抽象的,它不需要被實例化。
所有的trait方法,其實接收一個額外的參數(shù),即 this 對象。對對象的任何的訪問,如bottom等操作,實際上是直接調(diào)用對象的相應操作。
所有的trait方法,都是static的。
TestTrait$Rectangle.class
這個就是Rectangle這個類的代碼了。

// 首先,實現(xiàn)類以implements的方式繼承了trait所定義的接口。  public class test.TestTrait$Rectangle extends java.lang.Object implements test.TestTrait$Rectangular,scala.ScalaObject{   // 類的val屬性直接對應于一個同名的private字段和相應的讀取方法。  private final java.awt.Point bottomRight;   private final java.awt.Point topLeft;   // scala對象比較特殊的是,相應字段的初始化比調(diào)用父類構造函數(shù)來得更早。也就是說,在Class(arg)中的參數(shù)是最早被初始化的。  // 在構造函數(shù)后,可以看到,會調(diào)用trait的初始化代碼。當然,在我們的這個例子中,trait沒有任何的初始化行為。  public test.TestTrait$Rectangle(java.awt.Point, java.awt.Point);    Code:     0:   aload_0     1:   aload_1     2:   putfield        #13; //Field topLeft:Ljava/awt/Point;     5:   aload_0     6:   aload_2     7:   putfield        #15; //Field bottomRight:Ljava/awt/Point;     10:  aload_0     11:  invokespecial   #20; //Method java/lang/Object."":()V     14:  aload_0     15:  invokestatic    #26; //Method test/TestTrait$Rectangular$class.$init$:(Ltest/TestTrait$Rectangular;)V     18:  return  // height這個函數(shù)是從trait中繼承的,在這里,繼承體現(xiàn)為對trait實現(xiàn)類的一個調(diào)用,同時,將對象本身作為this傳遞給該函數(shù)  public int height();    Code:     0:   aload_0     1:   invokestatic    #39; //Method test/TestTrait$Rectangular$class.height:(Ltest/TestTrait$Rectangular;)I     4:   ireturn

這里不再羅列其他的函數(shù)實現(xiàn),其基本與height函數(shù)是相一致的。

理解了以上的邏輯,trait是如何實現(xiàn)將接口和接口實現(xiàn)溶于一體的,應該就非常的清楚了。我以前一直在納悶一個問題:接口中不能夠包含實現(xiàn)代碼,那么,難道每次編譯繼承trait的類時,這寫實現(xiàn)的代碼是怎么在子類中繼承的呢?難道是編譯器將這個邏輯復制了一份?如果這樣,不僅生成的代碼量很大,而且,還有一個問題,那就是,在編譯時需要有trait的源代碼才行。經(jīng)過上面的剖析,我們終于知道scala其實有更***的解決之道的:那就是一個trait輔助類。

Inside Scala - 4: Trait Stacks

這個例子摘自 Programming In Scala 這本書第12.5節(jié)。本文將從另外一個角度來分析 Stackable Trait的內(nèi)部原理。

package test

import scala.collection.mutable.ArrayBuffer   object Test7 {     abstract class IntQueue {      def put(x:Int)      def get(): Int    }        class BasicIntQueue extends IntQueue {      private val buf = new ArrayBuffer[Int]      def put(x:Int) { buf += x }      def get() = buf.remove(0)    }     trait Doubling extends IntQueue {      abstract override def put(x:Int) { super.put(2*x) }    }     def main(args: Array[String]) {      val queue: IntQueue = new BasicIntQueue with Doubling      queue.put(1)      queue.put(5)      println( queue.get )      println( queue.get )    }   }

我們來看這一行代碼 val queue = new BasicIntQue with Doubling,Scala針對這一行代碼干了很多很多的工作,并不是一個簡單的操作那么簡單
Scala需要新生成一個類型,在我的環(huán)境中,這個類叫做:Test7$$anon$1,看看這個代碼:
// 新的類以BasicIntQueue為父類,同時實現(xiàn)了Doubling這個trait定義的接口

public final class test.Test7$$anon$1 extends test.Test7$BasicIntQueue implements test.Test7$Doubling{   public test.Test7$$anon$1();    Code:     0:   aload_0     1:   invokespecial   #10; //Method test/Test7$BasicIntQueue."":()V // 父類初始化     4:   aload_0     5:   invokestatic    #16; //Method test/Test7$Doubling$class.$init$:(Ltest/Test7$Doubling;)V // trait輔助類初始化     8:   return  public void put(int);    Code:     0:   aload_0     1:   iload_1     2:   invokestatic    #21; //Method test/Test7$Doubling$class.put:(Ltest/Test7$Doubling;I)V // 這個類使用的是Doubling提供的版本     5:   return  public final void test$Test7$Doubling$$super$put(int); // Doubling所需要的super的版本    Code:     0:   aload_0     1:   iload_1     2:   invokespecial   #29; //Method test/Test7$BasicIntQueue.put:(I)V     5:   return  }

我們來分析一下Doubling這個trait的實現(xiàn)

public interface test.Test7$Doubling extends scala.ScalaObject{   public abstract void put(int);  // 這個是trait中實現(xiàn)的方法   public abstract void test$Test7$Doubling$$super$put(int); // 這個是這個trait 額外依賴的方法   }   // Doubling這個trait的輔助類  public abstract class test.Test7$Doubling$class extends java.lang.Object{  public static void $init$(test.Test7$Doubling);    Code:     0:   return  public static void put(test.Test7$Doubling, int);    Code:     0:   aload_0     1:   iconst_2     2:   iload_1     3:   imul     4:   invokeinterface #17,  2; //InterfaceMethod test/Test7$Doubling.test$Test7$Doubling$$super$put:(I)V  // 這也是 Doubling這個接口中需要 super.init這個方法的原因。     9:   return  }

由此可見,編譯器在處理 val queue: IntQueue = new BasicIntQueue with Doubling這一行代碼時,需要確定類、Trait的先后順序。這也是理解Trait的最為復雜的一環(huán)。后續(xù),我將就這個問題進行分析。

Inside Scala - 5: Trait Stacks

繼續(xù)上一個案例,現(xiàn)在我們將Trait的鏈搞得更長一些:

trait Incrementing extends IntQueue {  abstract override def put(x: Int) { super.put(x + 1) }  }  trait Filtering extends IntQueue {  abstract override def put(x: Int) {  if (x >= 0) super.put(x)  }  }   val queue: IntQueue = new BasicIntQueue with Incrementing with Filtering

新的類如何呢?當我們調(diào)用 queue的 put方法時,這個的先后順序究竟如何呢?還是看看生成的代碼:

public final class test.Test7$$anon$1 extends test.Test7$BasicIntQueue implements test.Test7$Incrementing,test.Test7$Filtering{   // 初始化的順序:先父類、再Incremeting、再Filtering,這個順序與源代碼的順序是一致的。  public test.Test7$$anon$1();    Code:     0:   aload_0     1:   invokespecial   #10; //Method test/Test7$BasicIntQueue."":()V     4:   aload_0     5:   invokestatic    #16; //Method test/Test7$Incrementing$class.$init$:(Ltest/Test7$Incrementing;)V     8:   aload_0     9:   invokestatic    #21; //Method test/Test7$Filtering$class.$init$:(Ltest/Test7$Filtering;)V     12:  return  // put 方法實際使用的是 Filtering這個Trait的put  public void put(int);    Code:     0:   aload_0     1:   iload_1     2:   invokestatic    #34; //Method test/Test7$Filtering$class.put:(Ltest/Test7$Filtering;I)V     5:   return  // Filtering Trait的父實現(xiàn)是Incremeting trait  public final void test$Test7$Filtering$$super$put(int);    Code:     0:   aload_0     1:   iload_1     2:   invokestatic    #38; //Method test/Test7$Incrementing$class.put:(Ltest/Test7$Incrementing;I)V     5:   return  // incrementing的父實現(xiàn)是父類的實現(xiàn)。  public final void test$Test7$Incrementing$$super$put(int);    Code:     0:   aload_0     1:   iload_1     2:   invokespecial   #26; //Method test/Test7$BasicIntQueue.put:(I)V     5:   return  }

因此,要理解這個過程,可以這么來分析:val queue: IntQueue = new BasicIntQueue with Incrementing with Filtering
首先初始化的是BasicIntQueue
在這個基礎上疊加 Incrementing,super.put引用的是BasicIntQueue的put方法
再在疊加后的基礎上疊加 Filtering,super.put引用的是 Incrementing的put方法
疊加后的結果就是***的版本。put引用的是Filtering的put方法
因此,初始化的順序是從左至右,而方法的可見性則是從右至左(可以理解為上面的疊加關系,疊加之后,上面的trait具有更大的優(yōu)先可見性。

Inside Scala - 6:Case Class 與 模式匹配

本文將嘗試對Case Class是如何參與模式匹配的進行剖析。文中的代碼還是來自 Programming In Scala一書。

abstract class Expr;  case class Var(name: String) extends Expr;  case class Number(num: Double) extends Expr;  case class UnOp(operator: String, arg: Expr) extends Expr;  case class BinOp(operator:String, left: Expr, right: Expr) extends Expr;

這里我們先來看一個最為簡單的模式匹配

some match {    case Var(name) => println("a var with name:" + name)  }


這幾行的代碼編譯后等效于:

if(some instanceof Var)  {      Var temp21 = (Var)some;      String name = temp21.name();      if(true)      {          name = temp22;          Predef$.MODULE$.println((new StringBuilder()).append("a var with name:").append(name).toString());      } else     {          throw new MatchError(some.toString());      }  } else {      throw new MatchError(some.toString());  }


如果從生成的代碼的角度上來看,Scala生成的代碼質(zhì)量并不高,其中的 if(true) else 的那個部分就有明顯的廢代碼。(不過,這個對運行效率的影響到時幾乎可以忽略,只是編譯后的字節(jié)碼倒是沒理由的多了幾分)。
上面的這個模式匹配僅僅是匹配一個類型。因此,其對應的java原語就是 instanceof 檢測。

讓我們更進一步, 看看如下的例子:

some match {    case Var("x") => println("a var with name:x")  }

這個模式匹配不僅匹配類型,還要匹配構造器中的name屬性為 "x"常量。這里我就不在福州 Scala生成的字節(jié)碼了,而是簡單的翻譯一下:
if( some instanceof Var)  -- 類型檢查
var.name() == "x"             -- 檢查 對象的 name 屬性是否等于 "x",編譯器非常清楚的指導 Case Class的每一個構造參數(shù)所對應的字段名稱。

更進一步,讓我們看看一個更復雜的模式匹配:嵌套的對象。

some match {    case BinOp("+", Var("x"), UnOp("-", Number(num))) => println("x - " + num)  }

這個邏輯其實也是上面的一個嵌套:
some instanceof BinOp
some.operator == "+"  編譯器進行了特殊的null檢測,以防止這個操作出現(xiàn)NPE
some.left instanceof Var
some.left.name == "x"
some.right instanceof UnOp
some.right.operator == "-"
some.right.arg instanceof Number
......
實際上,Scala的模式匹配確實為我們干了很多很多的事情,這也使得在很多的情況下,使用scala的模式匹配為我們提供了一個非常安全的(不用擔心大量的Null檢查),以及非常復雜的匹配操作。當然,與更復雜的模式匹配相比(譬如,規(guī)則引擎其實也是一個模式匹配的引擎),Scala的模式匹配還是相對比較簡單的。

這里簡單的補充一下 Scala中的幾種模式:
1、通配符模式。 也就是說使用 case _ => 來匹配所有的東西。或者,case Var(_) 來對局部進行通配。
2、常量匹配。譬如上述的Var("x") ,其中,"x"就是一個常量。常量除了文字常量外,還可以使用以大寫字母開頭的scala變量,或者`varname`形式的引用。
3、變量匹配。一個變量匹配實際上匹配任何的類型,并同時賦予其一個變量名。
4、構造函數(shù)匹配。匹配一個給定的類型,并且嵌套的對其參數(shù)進行匹配。參數(shù)可以是通配符模式、常量、變量或者子構造函數(shù)匹配
5、對于List類型, _*可以匹配剩余的全部元素。
6、Tuple匹配。(a,b,c)
7、類型匹配。對于java對象,由于并不適合Scala的Case Class模型,因此,可以使用類型進行匹配。在這種情況下,與構造子匹配是不同的。

再摘一段我以前編寫的使用scala來編寫應用程序的邏輯代碼,讓我們看看模式匹配在商業(yè)應用中的使用:

_req.transType match {        case RechargeEcp | RechargeGnete | FreezeToAvailable => // 充值類交易          assert(_req.amount > 0, "金額不正確")        case DirectPay | AvailableToFreeze =>    // 支付、凍結類交易          assert(_req.amount < 0, "金額不正確")        case _ =>              assert(false, "無效交易類型")      }            val _account = queryEwAccount(_req.userId)      assert(_account != null, "用戶尚未開通電子錢包")            var _accAvail, _accFreeze: EWSubAccount = null     var _total: BigDecimal = _req.amount      _account.subAccounts.find(_.subTypeCode==Available) match {        case Some(x) =>  _accAvail = x;    _total += x.balance        case None =>      }      _account.subAccounts.find(_.subTypeCode==Freeze) match {        case Some(x) =>  _accFreeze = x;    _total += x.balance        case None=>      }

這個僅僅是一個很簡單的應用,試想使用Java的if/else或者switch來進行相同的代碼,你不妨看看代碼量會增加多少?可讀性又會如何呢?

Scala Actor是一種借鑒于Erlang的進程消息機制的并發(fā)編程模式,由于Java中不存在Erlang的進程的概念,因此,Scala的Actor在隔離性上是不如Erlang的,譬如,在Erlang中,可以有效的終止一個進程,不僅僅無需擔心死鎖(根本沒有鎖),也可以馬上釋放掉改進程的內(nèi)存,這種隔離性在某種程度上是更接近于操作系統(tǒng)的進程的。在Java的世界里暫時沒有等效的替代品。

(題外話,最近在我們的Open Service Platform中集成了一個類似于操作系統(tǒng)定時調(diào)度的機制,可以定時執(zhí)行一些任務,但是***,我們?nèi)匀粵Q定將部分非交易相關的定時任務,主要是一些日志分析類、管理性批量處理等定時任務放到操作系統(tǒng)上進行調(diào)度,畢竟操作系統(tǒng)提供了一個更好的虛擬機,在OSGi層面仍然是有限的隔離,哪一天JVM能夠提供像操作系統(tǒng)的隔離特性,那么,操作系統(tǒng)就真的不重要了)。

本文將對actor的機制進行簡單的分析,以幫助加強對actor的理解。

package learn.actor   object Test1 extends Application {     import scala.actors.Actor._     val actor1 = actor {       println("i am in " + Thread.currentThread)       while(true) {         receive {           case msg => println("recieve msg:" + msg + " In " + Thread.currentThread);         }       }     }         val actor2 = actor {       println("i am in " + Thread.currentThread)       while(true) {         receive {           case msg: String => println("recieve msg:" + msg.toUpperCase + " In " + Thread.currentThread);         }       }     }         actor1 ! "Hello World"    actor2 ! "Hello World"    actor1 ! "ok"    actor2 ! "ok"      }

運行的結果是:

i am in Thread[pool-1-thread-1,5,main]  i am in Thread[pool-1-thread-2,5,main]  recieve msg:HELLO WORLD In Thread[pool-1-thread-2,5,main]  recieve msg:Hello World In Thread[pool-1-thread-1,5,main]  recieve msg:OK In Thread[pool-1-thread-2,5,main]  recieve msg:ok In Thread[pool-1-thread-1,5,main]

從這個例子來看,actor1和actor2實際上是兩個獨立的Java線程,任何線程可以將消息以 ! 的方式發(fā)給給這個線程進行處理。由于采用消息的方式來進行通信,因此,線程與線程之間無需采用Java的notify/wait機制,而后者是建立在鎖的基礎之上的。有關于這一點,我不在本文只進行深入的分析了。(有必要的話,我會再寫一個帖子來說明)。

那么 Scala Actor 的底層基礎是什么呢?與Java的notify/wait就完全沒有關系嗎?我們將重點分析actor的三個方法:!, receive, react

1、Scala Actor的send(外部調(diào)用者發(fā)送一個消息給當前actor)和receive(當前actor接收一個消息),這兩個操作是同步的(synchronized),也就是說,不可同時進入。(客觀的說,這一塊應該有很大的優(yōu)化空間,應該采用樂觀鎖的機制,可能會有更好的效率,一來,send/receive操作本身都是很快速的操作,即便在出現(xiàn)沖突的情況下,使用樂觀鎖也可以降低線程切換引起的開銷,而且,在大部分情況下,send操作與receive操作引發(fā)沖突的可能性并不是很大的。也就是說,在很大的程度上,send和receive還可以有更好的并行性,不知道后續(xù)的scala版本是否會進行優(yōu)化。)

2、執(zhí)行send操作時,如果當前actor正在等待這個消息(指actor自身已經(jīng)在receive、react并且期待這個消息的情況下),那么原來的等待將會馬上執(zhí)行,否則,消息會進入到actor的郵箱,等待下次receive/react的處理。這種模式相較于全部放入郵箱更加有效。它避免了一次在郵箱上的同步等待。

3、當執(zhí)行receive操作時,actor會檢查對象的郵箱,如果有匹配的消息的話,則會馬上返回該消息進行處理,否則會處在等待狀態(tài)(當前線程阻塞,采用的是wait原語)當匹配的消息到達時,也是采用notify原語通知等待線程繼續(xù)actor的處理的。

4、react與receive不同的是,react從不返回。這個在Java的編程世界里,好像還沒有看到類似的東西,該如何理解它呢:

react(f: ParticialFunction[Any,Unit]) 首先檢查actor的郵箱,如果有符合f的消息,則馬上提取該消息,并且在一個ExecutionPool中調(diào)度執(zhí)行f。(因此,f的執(zhí)行肯定不在請求react這個線程中執(zhí)行的。當前的調(diào)用react的線程,將產(chǎn)生一個 SuspendActorException,從而中斷一般的執(zhí)行過程。(也就是說文檔中說的不返回的概念)

如果當前郵箱中沒有消息,react將登記一個Continuation對象,將等待的消息(一個等待給定消息的函數(shù))、獲得消息后需要繼續(xù)進行的處理在actor中進行登記,而后,當前線程會產(chǎn)生一個SuspendActorException,中斷處理(從而是將當前線程歸還到線程池)。

當消息到達(通過send)時,send將檢查等待消息的Continuation,如過匹配的話,則會在線程池中的選擇一個線程來執(zhí)行f函數(shù)。在f處理完成一個消息后,一般的,它會再次調(diào)用 react來處理下一個消息,將再次重復這個過程。

應該說,scala的這個設計是非常精巧,也非常有效的,但這對Java開發(fā)程序員來說,就意味著一個新的挑戰(zhàn):看上去的一個函數(shù)體,實際上其中的代碼不僅是執(zhí)行不連續(xù)的(如closure可能會延遲、重復多次的被調(diào)用),甚至可能是在不同的線程中被執(zhí)行的。

從這個概念上來看,scala的actor并不對應于Java的線程,相反,可以理解為一個行為執(zhí)行者,是一個有上下文的非操作系統(tǒng)線程,語義其實更接近于現(xiàn)實的一個載體。這個與Erlang的進程還是有很明顯的語義上的區(qū)別的。從上述的分析中,或許如果切換到樂觀鎖的機制,Scala的并發(fā)效率還能有更進一步的提升。

到此,關于“Scala中Trait有什么作用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
新聞標題:Scala中Trait有什么作用
文章分享:http://weahome.cn/article/jiohcj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部