2016年7月28日 星期四

[Scala IA] The Basics : Ch3. OOP in Scala - Classes and constructors

Classes and constructors 
To connect to the already running MongoDB server, create a Mongo client class with a hostname and port number
  1. class MongoClient(val host:String, val port:Int)  
The class declaration looks different from the way you declare in Java or C#—you’re not only declaring the class, but also its primary constructor. 

The primary constructor is a constructor that needs to be called directly or indirectly from overloaded constructors when creating the instance MongoClient. You’ll look into overloaded constructors shortly. In Scala, the primary constructor for a class is coded inline with the class definition. In this case, the constructor takes two parameters: host and port. The host parameter specifies the address of the server, and port specifies the port in which the MongoDB server is waiting for the connection. 

Because all the constructor parameters are preceded by val, Scala will create immutable instance values for each of them. The following example creates an instance of a Mongo client and accesses its properties: 
scala> val client = new MongoClient("127.0.0.1", 123)
client: MongoClient = MongoClient@128d857d

scala> client.port
res0: Int = 123 
scala> client.host
res1: String = 127.0.0.1

Like Java or C#, Scala also uses the new keyword for creating instances of a class. But wait a minute—where’s the body of the MongoClient class? In Scala that’s optional. You can create classes without any class body. Creating a class like a JavaBean with only a getter and setter would be easy in Scala, as in the following: 
scala> class AddressBean(var address1:String, var address2:String, var city:String, var zipCode:Int)
defined class AddressBean

scala> var localAddress = new AddressBean("address1", "", "Taipei", 886)
localAddress: AddressBean = AddressBean@43fa0fc6

When parameters are prefixed with var, Scala creates mutable instance variables. The val and var prefixes are optional, and when both of them are missing, they’re treated as private instance values, not accessible to anyone outside the class: 



Note that when Scala creates instance values or variables, it also creates accessors for them. At no point in time are you accessing the field directly. The following MongoClient definition is equivalent to the class MongoClient(val host:String, val port:Int) definition. 

  1. class MongoClient(private val _host:String, private val _port:Int) {  
  2.     def host = _host  
  3.     def port = _port  
  4. }  
The reason I’m using private (you’ll learn about access levels later in this chapter) is so the Scala compiler doesn’t generate accessors by default. What val and var do is define a field and a getter for that field, and in the case of varan additional setter method is also created. 


Most of the time you’ll have MongoDB running on the localhost with default port 27017. Wouldn’t it be nice to have an additional zero-argument constructor that defaults the host and port number so you don’t have to specify them every time? How about this: 
  1. class MongoClient(val host:String, val port:Int) {  
  2.     def this() = this("127.0.0.1"27017)  
  3. }  
To overload a constructor, name it this followed by the parameters. Constructor definition is similar to method definition except that you use the name this. Also, you can’t specify a return type as you can with other methods. The first statement in the overloaded constructors has to invoke either other overloaded constructors or the primary constructor. The following definition will throw a compilation error: 
  1. class MongoClient(val host:String, val port:Int) {  
  2.     def this() = {  
  3.         val defaultHost = "127.0.0.1"  
  4.         val defaultPort = 27017  
  5.         this(defaultHost, defaultPort)  
  6.     }  
  7. }  
When you compile this with scalac, you get the following compilation errors: 


This poses an interesting challenge when you have to do some operation before you can invoke the other constructor. Later in this chapter, you’ll look into a companion object and see how it addresses this limitation. To make a connection to the MongoDB you’ll use the com.mongodb.Mongo class provided by the Mongo Java driver: 
  1. package ch3  
  2.   
  3. import com.mongodb.Mongo  
  4.   
  5. class MongoClient(val host:String, val port:Int) {  
  6.   private val underlying = new Mongo(host, port)  
  7.   def this() = this("127.0.0.1"27017)  
  8. }  
The underlying instance value will hold the connection to MongoDB. When Scala generates the constructor, it instantiates the underlying instance value too. Because of Scala’s scripting nature, you can write code inside the class like a script, which will get executed when the instance of the class is created (kind of like Ruby). The following example creates a class called MyScript that validates and prints the constructor input: 
  1. class MyScript(host:String) {  
  2.   require(host != null"Have to provide host name")  
  3.   if(host == "127.0.0.1") println("host = localhost")  
  4.   else println("host = " + host)  
  5. }  
And now load MyScript into Scala REPL: 
scala> :load MyScript.scala
Loading MyScript.scala...
defined class MyScript


scala> val s = new MyScript("127.0.0.1")
host = localhost
s: MyScript = MyScript@1f119598


scala> val s = new MyScript(null)
java.lang.IllegalArgumentException: requirement failed: Have to provide host name
at scala.Predef$.require(Predef.scala:224)

How is Scala doing this? Scala puts any inline code defined inside the class into the primary constructor of the class. If you want to validate constructor parameters, you could do that inside the class (usually at the top of the class). Let’s validate the host in the MongoClient
  1. package ch3  
  2.   
  3. import com.mongodb.Mongo  
  4.   
  5. class MongoClient(val host:String, val port:Int) {  
  6.   require(host != null"You have to provide a host name")  
  7.   private val underlying = new Mongo(host, port)  
  8.   def this() = this("127.0.0.1"27017)  
  9. }  
Right now the MongoClient is using an underlying instance to hold the connection to MongoDB. Another approach would be to inherit from the com.mongodb.Mongo class, and in this case you don’t have to have any instance value to hold the connection to MongoDB. To extend or inherit from a superclass, you have to use the extends keyword. The following code demonstrates how it would look if you extended from the Mongo class provided by the Java driver: 
  1. package ch3  
  2.   
  3. import com.mongodb.Mongo  
  4.   
  5. class MongoClientV2(val host:String, val port:Int)   
  6.   extends Mongo(host, port){  
  7.   require(host != null"You have to provide a host name")  
  8.   def this() = this("127.0.0.1"27017)  
  9. }  
As shown in the previous example, you can also inline the definition of the primary constructor of a superclass. One drawback of this approach is that you can no longer validate the parameters of the primary constructor before handing it over to the superclass. 
NOTE. 
When you don’t explicitly extend any class, by default that class extends the scala.AnyRef class. scala.AnyRef is the base class for all reference types (see section 3.1).

Even though extending com.mongodb.Mongo as a superclass is a completely valid way to implement this driver, you’ll continue to use the earlier implementation because that will give you more control over what you want to expose from the Scala driver wrapper, which will be a trimmed-down version of the complete Mongo Java API. Before going any further, I’ll talk about Scala imports and packages. This will help you to work with the Mongo library and structure your code.

[Scala 小學堂] Scala Gossic : 繼續深入 - 進階型態 (抽象成員)

Source From Here 
抽象成員 
在這邊所謂的 抽象成員,指的是在抽象類別或特徵(Trait)所定義的成員,像是使用 valvar 宣告的資料成員(Field),或是使用 def 所宣告的方法。在抽象類別或特徵(Trait)中定義抽象資料成員,不需要提供初始值,定義抽象方法時,不需要提供實作,繼承的子類別必須提供資料成員初始值或者是方法實作。例如: 
  1. trait Something {  
  2.     val some: String  
  3.     def doSome: String  
  4. }  
  5.   
  6. class Other extends Something {  
  7.     val some = "some"  
  8.     def doSome = "do...some"  
  9. }  
如果是個 無參數方法,在子類別可以將之重新定義為 val 成員,這在 無參數方法 中曾經介紹過,不過對於抽象方法而言,子類別重新定義為 val 時,不需加上 override 修飾。例如: 
  1. trait Something {  
  2.     val some: String  
  3.     def data: String  
  4. }  
  5.   
  6. class Other extends Something {  
  7.     val some = "some"  
  8.     val data = "data"  
  9. }  
如果是個 var 抽象成員,在子類別中可以這麼定義其初始值: 
  1. trait Something {  
  2.     var some: String  
  3. }  
  4.   
  5. class Other extends Something {  
  6.     var some = "some"  
  7. }  
在 屬性存取方法 中介紹過,var 成員其實可以實作為特殊名稱的存取方法。所以事實上,你定義一個 var 抽象成員時,相當於定義一組抽象方法,例如上例中的 Something,相當於以下: 
  1. trait Something {  
  2.     def some: String  
  3.     def some_=(s: String)  
  4. }  
所以,對於一個抽象 var 成員,你可以如下實作沒有問題,編譯器並不會抱怨: 
  1. trait Something {  
  2.     var some: String  
  3. }  
  4.   
  5. class Other extends Something {  
  6.     private[this] var s = "some"  
  7.     def some: String = "XD"  
  8.     def some_=(s: String) { this.s = s }  
  9. }  
如果你想實作某個抽象類別,但不想要定義出類別名稱,也就是你想要以 匿名類別Anonymous class)的方式對抽象類別實作並實例化,則以下是個例子: 
  1. abstract class Something(val s: String) {  
  2.     def doSome: String  
  3. }  
  4.   
  5. val something = new Something("XD") { def doSome =  s + "...done" }  
  6.   
  7. println(something.doSome)  // XD...done  
以下則是個使用特徵的例子: 
  1. trait Something {  
  2.     def doSome: String  
  3. }  
  4.   
  5. val something = new Something { def doSome =  "XD...done" }  
  6.   
  7. println(something.doSome)  
抽象類別可以宣告主要建構式,但特徵不行,這決定了你是否透過主要建構式傳入參數來初始化抽象成員。就前兩個例子而言,結果沒什麼不同,但如果是以下的例子,需注意一下執行的順序: 
  1. abstract class Something(val s: String) {  
  2.     println("主要建構式")  
  3.     def doSome: String  
  4. }  
  5.   
  6. def doIt = {  
  7.     println("函式執行")  
  8.     "XD"  
  9. }  
  10.   
  11. val something = new Something(doIt) { def doSome =  s + "...done" }  
  12. println(something.doSome)  
在使用 匿名類別 實作 Something 並建立實例時,要傳入建構式參數,值是由 doIt 函式決定,所以 doIt 函式會先執行,接著是 Something 主要建構式,也就是會先顯示"函式執行",接著再顯示 "主要建構式",最後呼叫 something.doSome 時,顯示 "XD...done"。 

如果是以下的例子: 
  1. trait Something {  
  2.     println("主要建構式")  
  3.     def doSome: String  
  4. }  
  5.   
  6. def doIt = {  
  7.     println("函式執行")  
  8.     "XD"  
  9. }  
  10.   
  11. val something = new Something { def doSome =  doIt + "...done" }  
  12. println(something.doSome)  
這邊一樣以匿名類別實作 Something 並建構實例,此時會先執行 Something 主要建構式,也就是先顯示 "主要建構式",但在執行 something.doSome 時,才會執行 doIt 函式取得其結果,也就是顯示 "函式執行",接著與 "...done" 字串串接後傳回,也就是最後顯示 "XD...done"。或許你會覺得,放在 doSome 方法中的 doIt 很顯然地,必須呼叫 doSome 方法才會被執行不是嘛?是的!但如果是以下這個例子,就不是那麼明顯了: 
  1. trait Something {  
  2.     println("主要建構式")  
  3.     def doSome: String  
  4. }  
  5.   
  6. def doIt = {  
  7.     println("函式執行")  
  8.     "XD"  
  9. }  
  10.   
  11. val something = new Something { val doSome =  doIt + "...done" }  
  12. println(something.doSome)  
這邊一樣以 匿名類別 實作 Something 並建構實例,此時會先執行 Something 主要建構式,也就是先顯示 "主要建構式",接著執行匿名類別主要建構式,此時呼叫 doIt 以取得結果,所以會顯示 "函式執行",再來是 something.doSome 呼叫,所以最後是顯示 "XD...done"。

[ Python 文章收集 ] Timing and Profiling in IPython

Source From  Here   Preface   Timing and profiling code is all sorts of useful, and it’s also just good ol’ fashioned fun ( and sometimes s...