顯示具有 ScalaIA 標籤的文章。 顯示所有文章
顯示具有 ScalaIA 標籤的文章。 顯示所有文章

2016年8月31日 星期三

[Scala IA] The Basics : Ch3. OOP in Scala - Modifiers

Modifiers (p86) 
You’ve already seen a few modifiers in action, but let’s look deeper into them. Along with standard modifiers like private and protected, Scala has more modifiers and new abilities. 

The private modifier can be used with any definition, which means it’s only accessible in an enclosed class, its companion object, or a companion class. In Scala, you can qualify a modifier with a class or a package name. In the following example, the private modifier is qualified by class and package name: 
  1. package outerpkg.innerpkg  
  2.   
  3. class Outer {  
  4.     class Inner {  
  5.         private[Outer] def f() = "This is f"  
  6.         private[innerpkg] def g() = "This is g"  
  7.         private[outerpkg] def h() = "This is h"  
  8.     }  
  9. }  
Here, access to the f method can appear anywhere within the Outer class, but not outside it. The method g is accessible anywhere within outerpkg.innerpkg. It’s like the package private in Java. You could use it to make your methods available for unit tests in the same package. Because the h method is qualified with outerpkg, it can appear anywhere within outerpkg and its subpackages. Scala also lets you qualify the private modifier with thisprivate[this]. In this case, it means object private. And object private is only accessible to the object in which it’s defined. When members are marked with private without a qualifier, they’re called class-private

The protected modifier is applicable to class member definitions. It’s accessible to the defining class and its subclasses. It’s also accessible to a companion object of the defining class and companion objects of all the subclasses. Like the private modifier, you can also qualify the protected modifier with class, package, and this. By default, when you don’t specify any modifier, everything is public. Scala doesn’t provide any modifier to mark members as public

Like Java, Scala also provides an override modifier, but the main difference is that in Scala the override modifier is mandatory when you override a concrete member definition from the parent class. The overridemodifier can be combined with an abstract modifier, and the combination is allowed only for members of traits. This modifier means that the member in question must be mixed with a class that provides the concrete implementation. An example will demonstrate this fact. The following creates a DogMood trait (dogs are moody, you know) with an abstract greet method and an AngryMood trait that overrides it: 
  1. trait DogMood{  
  2.     def greet  
  3. }  
  4.   
  5. trait AngryMood extends DogMood {  
  6.     override def greet = {  
  7.         println("bark")  
  8.         super.greet  
  9.     }  
  10. }  
The problem with this code is the super.greet line. You can’t invoke the super greet method because it’s abstract. But super calls are important if you want your trait to be stackable so that it can get mixed in with other traits. In cases like this, you can mark the method with abstract override, which means it should be mixed in with some class that has the concrete definition of the greet method. To make this code compile, you have to add abstract along withoverride, as in the following: 
  1. trait AngryMood extends DogMood {  
  2.     abstract override def greet = {  
  3.         println("bark")  
  4.         super.greet  
  5.     }  
  6. }  
So you can use above design as below: 
  1. trait DogMood{  
  2.     def greet  
  3. }  
  4.   
  5.   
  6. trait AngryMood extends DogMood {  
  7.     abstract override def greet = {  
  8.         printf("Bark ~~ ")  
  9.         super.greet  
  10.     }  
  11. }  
  12.   
  13. class ADog extends DogMood{  
  14.     override def greet = { println("Wow---") }  
  15. }  
  16.   
  17. class BDog extends ADog with AngryMood  
  18.   
  19.   
  20. val bdog = new BDog()  
  21. bdog.greet   // Bark ~~ Wow---  
Scala has introduced a new modifier called sealed, which applies only to class definitions. It’s a little different from the final modifier; classes marked final in Scala can’t be overridden by subclasses. But classes marked sealed can be overridden as long as the subclasses belong to the same source file. You used sealed in a previous section when you created QueryOption case classes: 
  1. sealed trait QueryOption  
This is a common pattern when you want to create a defined set of subclasses but don’t want others to subclass it. 

Supplement 
Scala's Stackable Trait Pattern

2016年8月28日 星期日

[Scala IA] The Basics : Ch3. OOP in Scala - Named and default arguments and copy constructors

Named and default arguments and copy constructors (p83) 
Scala lets you specify method arguments using a named style. When you have methods or class constructors taking similar types of arguments, it becomes difficult to detect errors if you swap them by mistake. Let’s take the example of Person again. Instead of passing in an order of first name, last name, if we swap the order, Scala won’t complain: 
scala> case class Person(firstName:String, lastName:String)
defined class Person

scala> val p = Person("lastName", "firstName")
p: Person = Person(lastName,firstName)

Unfortunately, both parameters are of the same type, and the compiler can’t detect the mistake at compile time. But now, using named style arguments, you can avoid the problem: 
scala> val p = Person(lastName = "lastName", firstName = "firstName")
p: Person = Person(firstName,lastName)

The named arguments use the same syntax as a variable assignment, and when you use named arguments, the order doesn’t matter. You can mix the named arguments with positional arguments, but that’s usually a bad idea. When going for named arguments, always try to use a named style for all the arguments. The following example uses a named style for the first argument but not for the second. As mentioned earlier, it’s good practice to avoid this: 
scala> val p = Person(firstName = "firstName", "lastName")
p: Person = Person(firstName,lastName)

When using a named style, if the parameter name doesn’t match, the Scala compiler will complain about the value not being found. But when you override a method from a superclass, the parameters’ names don’t have to match the names in the superclass method. In this case, the static type of the method determines which names have to be used. Consider this example, where you have the Person trait and SalesPerson overriding the grade method and changing the parameter name in the process from years to yrs
scala> trait Person { def grade(years:Int): String }
defined trait Person
warning: previously defined object Person is not a companion to trait Person.
Companions must be defined together; you may wish to use :paste mode for this.


scala> class SalesPerson extends Person { def grade(yrs:Int) = "Senior" }
defined class SalesPerson

scala> val s = new SalesPerson
s: SalesPerson = SalesPerson@51776d39

scala> s.grade(yrs = 1)
res1: String = Senior

scala> s.grade(years = 1)
:15: error: not found: value years

Here years won’t work because the type of the s instance is SalesPerson. If you force the type variable to Person, then you can use years as a named argument. I know this is a little tricky to remember, so watch out for errors like this: 
scala> val s:Person = new SalesPerson
s: Person = SalesPerson@6514451b

scala> s.grade(years = 1)
res3: String = Senior

scala> s.grade(yrs = 1) // Currently variable s is as pointer of Person
:15: error: not found: value yrs

The value of the named argument could be an expression like a method or block of code, and every time the method is called, the expression is evaluated: 
scala> s.grade(years = {val x = 10; x + 1})
res4: String = Senior

The complementing feature to named arguments is default arguments. You’ve already seen one example of a default argument in the query example, where the last argument of the case class defaulted to NoOption
  1. case class Query(q: DBObject, option: QueryOption = NoOption) {  
  2.     def sort(sorting: DBObject) = Query(q, Sort(sorting, option))  
  3.     def skip(skip: Int) = Query(q, Skip(skip, option))  
  4.     def limit(limit: Int) = Query(q, Limit(limit, option))  
  5. }  
The default argument has the form argType = expression, and the expression part is evaluated every time the method uses the default parameter. If you create a Query instance using Skip, the default won’t be used: 
  1. val skipOption = Skip (10, NoOption)  
  2. val newQuery = Query(new BasicDBObject(), skipOption)  
One of the interesting uses of default arguments in Scala is in the copy method of case classes. Starting from Scala 2.8 on, along with the usual goodies, every case class has an additional method called copy to create a modified instance of the class. This method isn’t generated if any member exists with the same name in the class or in one of its parent classes. The following example uses the copy method to create another instance of the skipquery option, but with a Limit option instead of NoOption
scala> val skipOption = Skip(10, NoOption)
skipOption: Skip = Skip(10,NoOption())

scala> val skipWithLimit = skipOption.copy(anotherOption = Limit(10, NoOption))
skipWithLimit: Skip = Skip(10,Limit(10,NoOption))

The copy method is using a named argument to specify the parameter that you’d like to change. The copy method generated by the Scala compiler for the Skip case class looks like the following: 
  1. case class Skip(number: Int, anotherOption: QueryOption) extends QueryOption {  
  2.     def copy(number: Int = number, anotherOption: QueryOption = anotherOption) = {  
  3.         Skip(number, anotherOption)  
  4.     }  
  5. }  
As you can see, in the generated method all the parameters are defaulted to the value provided to the constructor of the class, and you can pick and choose the parameter value you want to change during the copy. If no parameters are specified, copy will create another instance with the same values: 
scala> Skip(10, NoOption) == Skip(10, NoOption).copy()
res22: Boolean = true

In Scala, invoking the == method is the same as calling the equals method. The == method is defined in the scala.Any class, which is the parent class for all classes in Scala.

2016年8月13日 星期六

[Scala IA] The Basics : Ch3. OOP in Scala - Case class

Case class (p78) 
Case classes are a special kind of class created using the keyword case. When the Scala compiler sees a case class, it automatically generates boilerplate code so you don’t have to do it. Here’s an example of a Person class: 
scala> case class Person(firstName:String, lastName:String)
defined class Person

In this code example, you’re creating a Person case class with firstName and lastName parameters. But when you prefix a class with case, the following things will happen automatically: 
* Scala prefixes all the parameters with val, and that will make them public value. But remember that you still never access the value directly; you always access through accessors.
* Both equals and hashCode are implemented for you based on the given parameters.
* The compiler implements the toString method that returns the class name and its parameters.
* Every case class has a method named copy that allows you to easily create a modified copy of the class’s instance. You’ll learn about this later in this chapter.
* A companion object is created with the appropriate apply method, which takes the same arguments as declared in the class.
* The compiler adds a method called unapply, which allows the class name to be used as an extractor for pattern matching (more on this later).
* A default implementation is provided for serialization:
scala> val me = Person("Lee", "John")
me: Person = Person(Lee,John)

scala> val myself = Person("Lee", "John")
myself: Person = Person(Lee,John)

scala> me.equals(myself)
res0: Boolean = true

scala> me.hashCode
res1: Int = -1614407238

scala> myself.hashCode
res2: Int = -1614407238

Now think about how many times you’ve created a data transfer object (DTO) with only accessors for the purpose of wrapping some data. Scala’s case classes will make that easier for you the next time. Both equals and hashCodeimplementations also make it safer to use with collections. 
NOTE. 
You’re allowed to prefix the parameters to the case class with var if you want both accessors and mutators. Scala defaults it to val because it encourages immutability.

Like any other class, a case class can extend other classes, including trait and case classes. When you declare an abstract case class, Scala won’t generate the apply method in the companion object. That makes sense because you can’t create an instance of an abstract class. You can also create case objects that are singleton and serializable: 
  1. trait Boolean  
  2. case object Yes extends Boolean  
  3. case object No extends Boolean  
Scala case classes and objects make it easy to send serializable messages over the network. You’ll see a lot of them when you learn about Scala actors. 
NOTE. 
From Scala 2.8 on, case classes without a parameter list are deprecated. If you have a need, you can declare your case class without a parameter. Use () as a parameter list or use the case object.

Let’s put your recently gained knowledge of case classes to use in the MongoDB driver. So far, you’ve implemented basic find methods in your driver. It’s great, but you could do one more thing to the driver to make it more useful. MongoDB supports multiple query options like Sort, Skip, and Limit that you don’t support in your driver. Using case classes and a little pattern matching, you could do this easily. You’ll add a new finder method to the collection to find by query and query options. But first, let’s define the query options you’re going to support: 
- QueryOption.scala 
  1. package ch3  
  2.   
  3. import com.mongodb.DBObject  
  4.   
  5. sealed trait QueryOption   
  6.   
  7. case object NoOption extends QueryOption  
  8. case class Sort(sorting: DBObject, anotherOption: QueryOption) extends QueryOption  
  9. case class Skip(number: Int, anotherOption: QueryOption) extends QueryOption  
  10. case class Limit(limit: Int, anotherOption: QueryOption) extends QueryOption  
Here you’re creating four options: SortSkipLimit, and NoOption. The NoOption case is used when no option is provided for the query. Each query option could have another query option because you’ll support multiple query options at the same time. The Sort option takes another DBObject in which users can specify sorting criteria. Note that all the option case classes extend an empty trait, and it’s marked as sealed. I’ll talk about modifiers in detail later in the chapter, but for now a sealed modifier stops everyone from extending the trait, with a small exception. To extend a sealed trait, all the classes need to be in the same source file

For the Query class, you’ll wrap your good old friend DBObject and expose methods like sort, skip, and limit so that users can specify query options: 
- Query.scala 
  1. package ch3  
  2.   
  3. import com.mongodb.DBObject  
  4.   
  5. case class Query(q: DBObject, option: QueryOption = NoOption) {  
  6.   def sort(sorting: DBObject) = Query(q, Sort(sorting, option))  
  7.   def skip(skip: Int) = Query(q, Skip(skip, option))  
  8.   def limit(limit: Int) = Query(q, Limit(limit, option))  
  9. }  
Here each method creates a new instance of a query object with an appropriate query option so that, like a fluent interface (http://martinfowler.com/bliki/Fluent Interface.html), you can chain the methods together as in the following: 
  1. var rangeQuery = new BasicDBObject("i"new BasicDBObject("$gt"20))  
  2. var richQuery = Query(rangeQuery).skip(20).limit(10)  
Here you’re searching documents for which the i > 20 condition is true. From the result set you skip 20 documents and limit your result set to 10 documents. The most extraordinary part of the code is the last parameter of the Queryclass: option: QueryOption = NoOption. Here you’re assigning a default value to the parameter so that when the second parameter isn’t specified, as in the previous snippet, the default value will be used. You’ll look at default parameters in the next section. I’m sure that, as a focused reader, you’ve already spotted the use of the companion object that Scala generates for case classes. When creating an instance of a case class, you don’t have to use new because of the companion object. To use the new query class, add the following new method to the ReadOnly trait: 
  1. def find (query: Query) = { "..." }  
Before discussing implementation of the find-by-query method, let’s see how case classes help in pattern matching. You’ll be using pattern matching to implement the method. You learned about pattern matching in chapter 2, but I haven’t discussed case classes and how they could be used with pattern matching. One of the most common reasons for creating case classes is the pattern-matching feature that comes free with case classes. Let’s take the Person case class once again, but this time you’ll extract firstName and lastName from the object using pattern matching: 
scala> val p = Person("Lee", "John")
p: Person = Person(Lee,John)

scala> p match { case Person(first, last) => println(">>>> " + first + ", " + last) }
>>>> Lee, John

Look how you extracted the first and last names from the object using pattern matching. The case clause should be familiar to you; here you’re using a variable pattern in which the matching values get assigned to the first and lastvariables. Under the hood, Scala handles this pattern matching using a method called unapply. If you have to handcode the companion object that gets generated for Person, it will look like following: 
  1. object Person {  
  2.     def apply(firstName:String, lastName:String) = {  
  3.         new Person(firstName, lastName)  
  4.     }  
  5.     def unapply(p:Person): Option[(String, String)] =  
  6.         Some((p.firstName, p.lastName))  
  7.     }  
  8. }  
The apply method is simple; it returns an instance of the Person class and it is called when you create an instance of a case class. The unapply method gets called when the case instance is used for pattern matching.Typically, the unapply method is supposed to unwrap the case instance and return the elements (parameters used to create the instanceof the case class. I’ll talk about the Option type in Scala in detail in the next chapter, but for now think of it as a container that holds a value. If a case class has one element, the Option container holds that value. But because you have more than one, you have to return a tuple of two elements. 
NOTE. 
Sometimes instead of unapply, another method called unapplySeq could get generated if the case class parameters end with a repeated parameter (variable argument). I’ll discuss that in a later chapter.

In the discussion of for-comprehensions in chapter 2, I didn’t mention that the generator part of for-comprehensions uses pattern matching. I can best describe this with an example. Here you’re creating a list of persons and looping through them using pattern matching: 
scala> val people = List(Person("Simon", "Kish"), Person("Eric", "Weimer"))
people: List[Person] = List(Person(Simon,Kish), Person(Eric,Weimer))

scala> for(Person(first, last) <- first="" font="" last="" people="" yield="">
res4: List[String] = List(Simon,Kish, Eric,Weimer)

You’ll see more examples of extractors and pattern matching throughout the book. Before we leave this section, I still owe you the implementation of the find-by-query method, so here you go (see the following listing). 
- Listing 3.10 ReadOnly trait 
  1. trait ReadOnly {  
  2.   val underlying: MongoDBCollection  
  3.   def name = underlying getName  
  4.   def fullName = underlying getFullName    
  5.   def find(doc: DBObject): DBCursor = underlying find doc    // Explicitly specify return type  
  6.   def findOne(doc: DBObject) = underlying findOne doc  
  7.   def findOne = underlying findOne  
  8.   def getCount(doc: DBObject) = underlying getCount doc  
  9.   // Find method takes query object  
  10.   def find(query: Query): DBCursor = {  
  11.     def applyOptions(cursor:DBCursor, option: QueryOption): DBCursor = {  
  12.       option match {  
  13.         case Skip(skip, next) => applyOptions(cursor.skip(skip), next)  
  14.         case Sort(sorting, next)=> applyOptions(cursor.sort(sorting), next)  
  15.         case Limit(limit, next) => applyOptions(cursor.limit(limit), next)  
  16.         case NoOption => cursor  
  17.       }  
  18.     }  
  19.     applyOptions(find(query.q), query.option)  
  20.   }  
  21. }  
Here you’re using pattern matching to apply each query option to the result returned by the find method—in this case, DBCursor. The nested applyOptions function is applied recursively because each query option could wrap another query option identified by the next variable, and you bail out when it matches NoOption

When it comes to overload methods (methods with the same name), you have to specify the return type; otherwise, the code won’t compile. You have a similar limitation for recursive method calls. Scala type inference can’t infer the type of recursive methods or functions. In case of type errors, it’s always helpful to add type information. Using the test client in the following listing, you could test your new finder method. 

- Listing 3.11 TestFindByQuery.scala 
  1. import com.scalainaction.mongo._  
  2. import com.mongodb.BasicDBObject  
  3. def client = new MongoClient  
  4. def db = client.db("mydb")  
  5. val col = db.readOnlyCollection("test")  
  6. val updatableCol = db.updatableCollection("test")  
  7. for(i <- nbsp="" span="">1 to 100) updatableCol += new BasicDBObject("i", i)  
  8. val rangeQuery = new BasicDBObject("i"new BasicDBObject("$gt"20))  
  9. val richQuery = Query(rangeQuery).skip(20).limit(10)  
  10. val cursor = col.find(richQuery)  
  11. while(cursor.hasNext()) {  
  12.     println(cursor.next());  
  13. }  
 

Supplement 
Gossip@DesignPattern : Behavioral - Visitor 模式 
Scala Gossic : 繼續深入 - 模式比對 (案例類別)

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...