知识大全 Scala代码编写中常见的十大陷阱

Posted

篇首语:不积跬步,无以至千里;不积小流,无以成江海。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Scala代码编写中常见的十大陷阱相关的知识,希望对你有一定的参考价值。

Scala代码编写中常见的十大陷阱  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  对于支持并发和分布式处理 高可扩展 基于组件的应用程序来说 Scala的功能是很强大的 它利用了面向对象和函数式程序设计的优点 这种基于Java虚拟机的语言在宣布Twitter正使用它时受到了最多的冲击(相关 CTO评论 从Scala进驻Twitter看多语言混杂系统的前景) 如果使用正确 Scala可以大量减少应用程序对代码的需求

  对于Scala编程 我们收集了这些常见代码编写中的陷阱 这些技巧来自于Daniel Sobral 一个曾参加过FreeBSD项目和Java软件开发工程的Scala狂热爱好者

   语法错误

  认为  yield 像 return 一样 有人会这样写

   for(i < to )

     if (i % == )

       yield i

     else

       yield i

  

  正确的表示应该是

   for(i < to )

   yield

     if (i % == )

       i

     else

       i

  

   误用和语法错误

  滥用scala xml XML loadXXX 这个的语法分析器试图访问外部的DTD strip组件或类似的东西 在scala xml parsing ConstructingParser fromXXX中有另一个可选的语法分析器 同时 在处理XML时忘记了等号两端的空格 比如

   val xml=<root/>

  这段代码真正的意思是

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

  

  这种情况的发生是由于操作符相当随意 而且scala采用这样一种事实 字母数字字符与非字母数字字符通过下划线可以结合成为一个有效的标识符 这也使得 x+y 这样的表达式不会被当成一个标识符 而应该注意 x_+ 是一个有效的标识符 所以 赋值标识符的写法应该是

   val xml = <root/>

   用法错误

  为那些根本不是无关紧要的应用加入Application特征

   object MyScalaApp extends Application

     // body

  

  示例部分的问题在于 body部分在单元对象初始化时执行 首先 单元初始化的执行是异步的 因此你的整个程序不能与其它线程交互 其次 即时编译器(JIT)不会优化它 因此你的程序速度慢下来 这是没有必要的

  另外 不能与其它线程的交互也意味着你会忘记测试应用程序的GUI或者Actors

   用法错误

  试图模式匹配一个字符串的正则表达式 而又假定该正则表达式是无界的

   val r = (\\d+) r

   val s = > <

   s match

     case r(n) => println( This won t match )

     case _ => println( This will )

  

  此处的问题在于 当模式模式匹配时 Scala的正则表达式表现为如同开始于 ^ 结束于 $ 使之工作的正确写法是

   val r = (\\d+) r

   val s = > <

   r findFirstIn s match

     case Some(n) => println( Matches to +n)

     case _ => println( Won t match )

  

  或者确保模式能匹配任意前缀和后缀

   val r = *(\\d+) * r

   val s = > <

   s match

     case r(n) => println( This will match the first group of r +n+ to )

     case _ => println( Won t match )

  

   用法错误

  把var和val认为是字段(fields)

  Scala强制使用统一访问准则(Uniform Access Principle) 这使得我们无法直接引用一个字段 所有对任意字段的访问只能通过getters和setters val和var事实上只是定义一个字段 getter作为val字段 对于var则定义一个setter

  Java程序员通常认为var和val是字段 而当发现在他们的方法中它们共享相同的命名空间时 常常觉得惊讶 因此 不能重复使用它们的名字 共享命名空间的是自动定义的getter和setter而不是字段本身 通常程序员们会试图寻找一种访问字段的方法 从而可以绕过限制 但这只是徒劳 统一访问准则是无法违背的 它的另一个后果是 当进行子类化时val会覆蓋def 其它方法是行不通的 因为val增加了不变性保证 而def没有

  当你需要重载时 没有任何准则会指导你如何使用私有的getters和setters Scala编译器和库代码常使用私有值的别名和缩写 反之公有的getters和setters则使用fullyCamelNamingConventions(一种命名规范) 其它的建议包括 重命名 实例中的单元化 甚至子类化 这些建议的例子如下

  重命名

   class User(val name: String initialPassword: String)

     private lazy var encryptedPassword = encrypt(initialPassword salt)

     private lazy var salt = scala util Random nextInt

  

     private def encrypt(plainText: String salt: Int): String =

     private def decrypt(encryptedText: String salt: Int): String =

  

     def password = decrypt(encryptedPassword salt)

     def password_=(newPassword: String) = encrypt(newPassword salt)

  

  单例模式(Singleton)

   class User(initialName: String initialPassword: String)

      private object fields

        var name: String = initialName;

        var password: String = initialPassword;

     

      def name = fields name

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

      def password = fields password

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

  

  或者 对于一个类来说 可以为相等关系或hashCode自动定义可被重用的方法

   class User(name : String password : String)

     private case class Fields(var name: String var password : String)

     private object fields extends Fields(name password )

  

  

     def name = fields name

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

     def password = fields password

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

  

  子类化

   case class Customer(name: String)

  

   class ValidatingCustomer(name : String) extends Customer(name )

     require(name length < )

  

     def name_=(newName : String) =

       if (newName length < ) error( too short )

       else super name_=(newName)

  

  

   val cust = new ValidatingCustomer( xyz )

   用法错误

  忘记类型擦除(type erasure) 当你声明了一个类C[A] 一个泛型T[A]或者一个函数或者方法m[A]后 A在运行时并不存在 这意味着 对于实例来讲 任何参数都将被编译成AnyRef 即使编译器能够保证在编译过程中类型不会被忽略掉

  这也意味着在编译时你不能使用类型参数A 例如 下面这些代码将不会工作

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

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

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

     case _ => println( Something else )

  

  在运行时 被传递的List没有类型参数 而List[Int]和List[String]都将会变成List[_] 因此只有第一种情况会被调用

  你也可以在一定范围内不使用这种方法 而采用实验性的特性Manifest 像这样

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

     case int => println( List of Ints )

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

     case _ => println( Something else )

  

   设计错误

  Implicit关键字的使用不小心 Implicits非常强大 但要小心 普通类型不能使用隐式参数或者进行隐匿转换

  例如 下面一个implicit表达式

   implicit def string Int(s: String): Int = s toInt

  这是一个不好的做法 因为有人可能错误的使用了一个字符串来代替Int 对于上面的这种情况 更好的方法是使用一个类

   case class Age(n: Int)

   implicit def string Age(s: String) = Age(s toInt)

   implicit def int Age(n: Int) = new Age(n)

   implicit def age Int(a: Age) = a n

  这将会使你很自由的将Age与String或者Int结合起来 而不是让String和Int结合 类似的 当使用隐式参数时 不要像这样做

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

  这不仅因为它容易在隐式参数间产生冲突 而且可能导致在毫无提示情况下传递一个隐式的age 而接收者需要的只是隐式的Int或者其它类型 同样 解决办法是使用一个特定的类

  另一种可能导致implicit用法出问题的情况是有偏好的使用操作符 你可能认为 ~ 是字符串匹配时最好的操作符 而其他人可能会使用矩阵等价(matrix equivalence) 分析器连接等(符号) 因此 如果你使用它们 请确保你能够很容易的分离其作用域

   设计错误

  设计不佳的等价方法 尤其是

  ◆试着使用 == 代替 equals (这让你可以使用 != )

  ◆使用这样的定义

   def equals(other: MyClass): Boolean

  而不是这样的

   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

   用法错误

  在Unix/Linux/*BSD的系统中 对你的主机进行了命名却没有在主机文件中声明 特别的 下面这条指令不会工作

   ping `hostname`

  在这种情况下 fsc和scala都不会工作 而scalac则可以 这是因为fsc运行在背景模式下 通过TCP套接字监听连接来加速编译 而scala却用它来加快脚本的执行速度

   风格错误

  使用while 虽然它有自己的用处 但大多数时候使用for往往更好 在谈到for时 用它们来产生索引不是一个好的做法

  避免这样的使用

   def matchingChars(string: String characters: String) =

     var m =

     for(i < until string length)

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

         m += string(i)

     m

  

  而应该使用

   def matchingChars(string: String characters: String) =

     var m =

     for(c < string)

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

         m += c

     m

  

  如果有人需要返回一个索引 可以使用下面的形式来代替按索引迭代的方法 如果对性能有要求 它可以较好的应用在投影(projection)(Scala )和视图(Scala )中

   def indicesOf(s: String c: Char) = for

     (sc index) < s zipWithIndex

     if c == sc

cha138/Article/program/Java/hx/201311/26022

相关参考

知识大全 破解求职过程中常见的六大陷阱

职场新人急于找工作,往往会被心急冲昏头脑,或者因为缺乏社会经验忽略了一些求职盲点。新人们该如何擦亮双眼识破这些骗局,有如何的避免求职过程中的种种陷阱?告诉大家几种求职陷阱及他的破解方法:陷阱1不通过正

知识大全 编写JScript代码

编写JScript代码  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  与其他许多编程语言一样Mi

知识大全 编写高级JavaScript代码

Java应用:编写高级JavaScript代码  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  创

知识大全 编写代码生成器[1]

Eclipse工具开发:编写代码生成器[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nbs

知识大全 编写代码生成器[2]

Eclipse工具开发:编写代码生成器[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nbs

知识大全 编写代码生成器[6]

Eclipse工具开发:编写代码生成器[6]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nbs

知识大全 编写代码生成器[5]

Eclipse工具开发:编写代码生成器[5]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nbs

知识大全 编写代码生成器[4]

Eclipse工具开发:编写代码生成器[4]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nbs

知识大全 编写代码生成器[3]

Eclipse工具开发:编写代码生成器[3]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nbs

知识大全 编写高级JavaScript应用代码[1]

Java应用:编写高级JavaScript应用代码[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下