一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Scala代碼編寫中常見的十大陷阱

 gearss 2017-01-15

對于支持并發(fā)和分布式處理、高可擴展、基于組件的應用程序來說,Scala的功能是很強大的。它利用了面向對象和函數式程序設計的優(yōu)點。這種基于Java虛擬機的語言在宣布Twitter正使用它時受到了最多的沖擊(相關51CTO評論:從Scala進駐Twitter看多語言混雜系統(tǒng)的前景)。如果使用正確,Scala可以大量減少應用程序對代碼的需求。

對于Scala編程, 我們收集了這些常見代碼編寫中的陷阱。這些技巧來自于Daniel Sobral,一個曾參加過FreeBSD項目和Java軟件開發(fā)工程的Scala狂熱愛好者。

1. 語法錯誤

認為 “yield” 像 ”return” 一樣。有人會這樣寫:

1. for(i <- 0 to 10) {

2. if (i % 2 == 0)

3. yield i

4. else

5. yield -i

6. }

正確的表示應該是:

1. for(i <- 0 to 10)

2. yield {

3. if (i % 2 == 0)

4. i

5. else

6. -i

7. }

2. 誤用和語法錯誤

濫用scala.xml.XML.loadXXX。這個的語法分析器試圖訪問外部的DTD、strip組件或類似的東西。在scala.xml.parsing.ConstructingParser.fromXXX中有另一個可選的語法分析器。同時,在處理XML時忘記了等號兩端的空格。比如:

1. val xml=<root/>

這段代碼真正的意思是:

1. val xml.$equal$less(root).$slash$greater

2. 這種情況的發(fā)生是由于操作符相當隨意,而且scala采用這樣一種事實:字母數字字符與非字母數字字符通過下劃線可以結合成為一個有效的標識符。這也使得“x+y”這樣的表達式不會被當成一個標識符。而應該注意 “x_+”是一個有效的標識符。所以,賦值標識符的寫法應該是:

1. val xml = <root/>

3. 用法錯誤

為那些根本不是無關緊要的應用加入Application特征。

1. object MyScalaApp extends Application {

2. // ... body ...

3. }

Scala講座:函數式語言的體驗

Scala講座:類型系統(tǒng)和相關功能

Adobe架構師談Scala:功能強大但令人困惑

Scala 2.8的for表達式:性能與運行順序的

Scala Actor與底層并發(fā)編程機制異同之探

示例部分的問題在于,body部分在單元對象初始化時執(zhí)行。首先,單元初始化的執(zhí)行是異步的,因此你的整個程序不能與其它線程交互;其次,即時編譯器(JIT)不會優(yōu)化它,因此你的程序速度慢下來,這是沒有必要的。

另外,不能與其它線程的交互也意味著你會忘記測試應用程序的GUI或者Actors。

4. 用法錯誤

試圖模式匹配一個字符串的正則表達式,而又假定該正則表達式是無界的:

1. val r = """(\d+)""".r

2. val s = "--> 5 <---"

3. s match {

4. case r(n) => println("This won't match")

5. case _ => println("This will")

6. }

此處的問題在于, 當模式模式匹配時, Scala的正則表達式表現(xiàn)為如同開始于”^”,結束于”$”。使之工作的正確寫法是:

1. val r = """(\d+)""".r

2. val s = "--> 5 <---"

3. r findFirstIn s match {

4. case Some(n) => println("Matches 5 to "+n)

5. case _ => println("Won't match")

6. }

或者確保模式能匹配任意前綴和后綴:

1. val r = """.*(\d+).*""".r

2. val s = "--> 5 <---"

3. s match {

4. case r(n) => println("This will match the first group of r, "+n+", to 5")

5. case _ => println("Won't match")

6. }

5. 用法錯誤

把var和val認為是字段(fields):

Scala強制使用統(tǒng)一訪問準則(Uniform Access Principle),這使得我們無法直接引用一個字段。所有對任意字段的訪問只能通過getters和setters。val和var事實上只是定義一個字段,getter作為val字段,對于var則定義一個setter。

Java程序員通常認為var和val是字段,而當發(fā)現(xiàn)在他們的方法中它們共享相同的命名空間時,常常覺得驚訝。因此,不能重復使用它們的名字。共享命名空間的是自動定義的getter和setter而不是字段本身。通常程序員們會試圖尋找一種訪問字段的方法,從而可以繞過限制——但這只是徒勞,統(tǒng)一訪問準則是無法違背的。它的另一個后果是,當進行子類化時val會覆蓋def。其它方法是行不通的,因為val增加了不變性保證,而def沒有。

當你需要重載時,沒有任何準則會指導你如何使用私有的getters和setters。Scala編譯器和庫代碼常使用私有值的別名和縮寫,反之公有的getters和setters則使用fullyCamelNamingConventions(一種命名規(guī)范)。其它的建議包括:重命名、實例中的單元化,甚至子類化。這些建議的例子如下:

重命名

1. class User(val name: String, initialPassword: String) {

2. private lazy var encryptedPassword = encrypt(initialPassword, salt)

3. private lazy var salt = scala.util.Random.nextInt

4.

5. private def encrypt(plainText: String, salt: Int): String = { ... }

6. private def decrypt(encryptedText: String, salt: Int): String = { ... }

7.

8. def password = decrypt(encryptedPassword, salt)

9. def password_=(newPassword: String) = encrypt(newPassword, salt)

10. }

單例模式(Singleton)

1. class User(initialName: String, initialPassword: String) {

2. private object fields {

3. var name: String = initialName;

4. var password: String = initialPassword;

5. }

6. def name = fields.name

7. def name_=(newName: String) = fields.name = newName

8. def password = fields.password

9. def password_=(newPassword: String) = fields.password = newPassword

10. }

或者,對于一個類來說,可以為相等關系或hashCode自動定義可被重用的方法

1. class User(name0: String, password0: String) {

2. private case class Fields(var name: String, var password0: String)

3. private object fields extends Fields(name0, password0)

4.

5.

6. def name = fields.name

7. def name_=(newName: String) = fields.name = newName

8. def password = fields.password

9. def password_=(newPassword: String) = fields.password = newPassword

10. }  

子類化

1. case class Customer(name: String)

2.

3. class ValidatingCustomer(name0: String) extends Customer(name0) {

4. require(name0.length < 5)

5.

6. def name_=(newName : String) =

7. if (newName.length < 5) error("too short")

8. else super.name_=(newName)

9. }

10.

11. val cust = new ValidatingCustomer("xyz123")

6. 用法錯誤

忘記類型擦除(type erasure)。當你聲明了一個類C[A]、一個泛型T[A]或者一個函數或者方法m[A]后,A在運行時并不存在。這意味著,對于實例來講,任何參數都將被編譯成AnyRef,即使編譯器能夠保證在編譯過程中類型不會被忽略掉。

這也意味著在編譯時你不能使用類型參數A。例如,下面這些代碼將不會工作:

1. def checkList[A](l: List[A]) = l match {

2. case _ : List[Int] => println("List of Ints")

3. case _ : List[String] => println("List of Strings")

4. case _ => println("Something else")

5. }

在運行時,被傳遞的List沒有類型參數。 而List[Int]和List[String]都將會變成List[_]. 因此只有第一種情況會被調用。

你也可以在一定范圍內不使用這種方法,而采用實驗性的特性Manifest, 像這樣:

1. def checkList[A](l: List[A])(implicit m: scala.reflect.Manifest[A]) = m.toString match {

2. case "int" => println("List of Ints")

3. case "java.lang.String" => println("List of Strings")

4. case _ => println("Something else")

5. }

7. 設計錯誤

Implicit關鍵字的使用不小心。Implicits非常強大,但要小心,普通類型不能使用隱式參數或者進行隱匿轉換。

例如,下面一個implicit表達式:

1. implicit def string2Int(s: String): Int = s.toInt

這是一個不好的做法,因為有人可能錯誤的使用了一個字符串來代替Int。對于上面的這種情況,更好的方法是使用一個類。

1. case class Age(n: Int)

2. implicit def string2Age(s: String) = Age(s.toInt)

3. implicit def int2Age(n: Int) = new Age(n)

4. implicit def age2Int(a: Age) = a.n

這將會使你很自由的將Age與String或者Int結合起來,而不是讓String和Int結合。類似的,當使用隱式參數時,不要像這樣做:

1. case class Person(name: String)(implicit age: Int)

這不僅因為它容易在隱式參數間產生沖突,而且可能導致在毫無提示情況下傳遞一個隱式的age, 而接收者需要的只是隱式的Int或者其它類型。同樣,解決辦法是使用一個特定的類。

另一種可能導致implicit用法出問題的情況是有偏好的使用操作符。你可能認為”~”是字符串匹配時最好的操作符,而其他人可能會使用矩陣等價(matrix equivalence),分析器連接等(符號)。因此,如果你使用它們,請確保你能夠很容易的分離其作用域。

8. 設計錯誤

設計不佳的等價方法。尤其是:

◆試著使用“==”代替“equals”(這讓你可以使用“!=”)

◆使用這樣的定義:

1. def equals(other: MyClass): Boolean

而不是這樣的:

1. override def equals(other: Any): Boolean

◆忘記重載hashCode,以確保當a==b時a.hashCode==b.hashCode(反之不一定成立)。

◆不可以這樣做交換: if a==b then b==a。特別地,當考慮子類化時,超類是否知道如何與一個子類進行對比,即使它不知道該子類是否存在。如果需要請查看canEquals的用法。

◆不可以這樣做傳遞: if a==b and b ==c then a==c。

9. 用法錯誤

在Unix/Linux/*BSD的系統(tǒng)中,對你的主機進行了命名卻沒有在主機文件中聲明。特別的,下面這條指令不會工作:

1. ping `hostname`

在這種情況下,fsc和scala都不會工作,而scalac則可以。這是因為fsc運行在背景模式下,通過TCP套接字監(jiān)聽連接來加速編譯,而scala卻用它來加快腳本的執(zhí)行速度。

10.風格錯誤

使用while。雖然它有自己的用處,但大多數時候使用for往往更好。在談到for時,用它們來產生索引不是一個好的做法。

避免這樣的使用:

1. def matchingChars(string: String, characters: String) = {

2. var m = ""

3. for(i <- 0 until string.length)

4. if ((characters contains string(i)) && !(m contains string(i)))

5. m += string(i)

6. m

7. }

而應該使用:

1. def matchingChars(string: String, characters: String) = {

2. var m = ""

3. for(c <- string)

4. if ((characters contains c) && !(m contains c))

5. m += c

6. m

7. }

如果有人需要返回一個索引,可以使用下面的形式來代替按索引迭代的方法。如果對性能有要求,它可以較好的應用在投影(projection)(Scala 2.7)和視圖(Scala 2.8)中。

1. def indicesOf(s: String, c: Char) = for {

2. (sc, index) <- s.zipWithIndex

3. if c == sc

4. } yield index

參考資料

http://www./index.php/article/anzhuo/Android_Google_7024.html
更多精彩電腦信息,請登錄:中國高速網-IT頻道。
中國高速網-IT頻道:http://it./
中國高速網-軟件與下載頻道:http://soft.
中國高速網-服務器頻道:http://server./
中國高速網-網絡安全頻道:http://network./

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    亚洲中文在线观看小视频| 老司机精品福利视频在线播放| 久久99青青精品免费| 福利在线午夜绝顶三级| 人人妻在人人看人人澡| 色偷偷偷拍视频在线观看| 欧美不卡一区二区在线视频| 婷婷一区二区三区四区| 国产传媒精品视频一区| 激情爱爱一区二区三区| 欧美日韩一级aa大片| 欧美日韩国内一区二区| 欧美性高清一区二区三区视频| 国产精品一区二区视频大全| 亚洲一区二区精品国产av| 亚洲成人免费天堂诱惑| 国产麻豆成人精品区在线观看| 九九热视频经典在线观看| 国产福利一区二区久久| 在线视频三区日本精品| 国产毛片av一区二区三区小说| 又色又爽又无遮挡的视频| 夫妻性生活黄色录像视频| 欧美字幕一区二区三区| 青青草草免费在线视频| 久久精品亚洲精品一区| 欧美黑人在线精品极品| 久久精品国产在热亚洲| 精品老司机视频在线观看| 五月激情婷婷丁香六月网| 日韩欧美国产精品自拍| 国产亚洲精品岁国产微拍精品| 日韩精品综合免费视频| 日韩欧美黄色一级视频| 一本久道久久综合中文字幕| 国产成人亚洲综合色就色| 国产精品一区二区传媒蜜臀| 91人妻人人澡人人人人精品| av国产熟妇露脸在线观看| 中文字幕亚洲精品在线播放| 亚洲精品欧美精品日韩精品|