2014年6月4日 星期三

OOP in Scala 2, Packaging & Import

前言
物件導向的世界中, 需要有個方法來幫物件做分類管理, 區分功能性並建立架構, package 的概念很熟悉, 它也正是一個物件, 是用來定義複數物件的集合, JAVA的package很基本, 但缺乏彈性, 已知道的比如C#就有scope的package架構, 在Scala中更是講這兩者結合, 端看軟體需求, 讓使用者更方便建立物件




Package

直接來看一個Scala用scope模式來定義package
 package com {  
  package scalapackage {  
   package first {  
    class Person(val name:String, val age:Int) {  
     if( name.length <= 0)  
         print("need name!!")  
     def this() = this("TOM", 18)  
    }  
   }  
  }  
 }  

上述的code 就是把Person class定義在com.scalapackage.first package中, scope 的方法看起來很直覺; 或者你也可以用JAVA的方式
 package com.scalapackage.first   
 class Person(val name:String, val age:Int) {  
     if( name.length <= 0)  
         print("need name!!")  
     def this() = this("TOM", 18)  
 }  
或者在最外層加上大括號, 成一個scope
 package com.scalapackage.first {  
   class Person(val name:String, val age:Int) {  
       if( name.length <= 0)  
        print("need name!!")  
       def this() = this("TOM", 18)  
   }  
 }  


這是風格上的差別, scope的方法可以讓你很明確的布局class的架構, 不過當你在同一個Scala檔放入多重pacakge時候, 就有可能第一眼會混淆, 一般來說普遍還是用JAVA的傳統方式在 code file第一行定義好package架構

另一個你該注意的點是, Scala中的package的宣告可以不必和實際上的Filesystem的Folder structure一致, 可以很自由的在同一個scala file中多重宣告package:
 package com.scalapackage {  
  package first {  
    class Person  
  }   
  package second {  
   class Animal   
  }  
  package third {  
   class Mental  
  }  
 }  

把上述的code存在一個package.scala file, 然後用scalac 去compile它, 你可以發現scala compiler會自動幫你生成相對應的 Folder structure, 來確保符合JVM需求

Import

基本上Scala中的import keyword跟JAVA 很類似, 但在JAVA之外又加了很多彈性的應用

import package下的所有class用 底線 "_"

 import com.scalapackage._  

import 可以用在code file中任何位置

 scala> val randomValue = { import scala.util.Random  
      new Random().nextInt  
    }  
 randomValue: Int = 1453905449  
上述例子中是在code block 中import Random, 因此你也該明白它的 lexically scoped應該也只限制在該code block中, 以外地方是不知道有Random的!!

再者Scala Program是自動import scala package, 因此你可以直接import util.Random, 不必再加上scala
 scala> val randomValue = { import util.Random   
    new Random().nextInt   
   }  
 randomValue: Int = 619602925  

在Scala中 import 一個package, 在它底下的成員還有subpackage也會都 import進來

要import class的成員, 要加上底線 _  在class之後

 scala> import java.lang.System._  
 import java.lang.System._  
 scala> nanoTime  
 res0: Long = 1268518636387441000  
上述code可以直接call nanoTime method 就是因為你import java.lang.System._ 把System的member都拉進來, 這跟JAVA的static import很類似(但你該知道Scala是沒有static)

import name mapping

在Scala中當你在import該class時候, 可以定義一個新的名字去mapping 你所import 的class
這是Scala import中最重要的Feature, 當你可以控制import進來的class name, 大大的增加code的可讀性, 尤其當import namespace conflict

在JAVA中最常見的例子是 java.sql.Date 與 java.util.Date的名稱衝突問題, 你怎知道你同時import兩者時用的是誰的Date? 在Scala中你可以很簡單的解決:
 import java.util.Date  
 import java.sql.{Date => SqlDate}  
 import RichConsole._  
 val now = new Date  
 p(now)  
 val sqlDate = new SqlDate(now.getTime)  
 p(sqlDate)  

透過大括號, 把sql的Date 名稱轉換成 SqlDate, 後者call Date會很明白是屬於 util package的. 如此減少class 名稱的混淆, 更進一步的, 你還可以hide 已經 import的class, 避免造成衝突:
 import java.sql.{Date => _ }  
如此一來, sql package的Date就不會被看見了, 你只會call 到 util Date


下一章

沒有留言:

張貼留言