Showing posts with label object. Show all posts
Showing posts with label object. Show all posts

Thursday, November 5, 2009

Access modifiers (public, private, protected)

By default classes, objects and class members (fields, methods) are all public.
IE:
  1. object PublicObject {
  2.   val publicVal
  3.   var publicVar
  4.   def publicMethod = 1
  5. }

In this example everything is public. Similar to Java there is private and protected (there is no public because that is default). Private works as it does in Java but protected is dramatically different.
  • The first difference is that protected can have two forms: protected and protected[foo]. Where foo can be a class, package or object.

  • The second difference is that the non-parameterized protected for is only visible from subclasses not from the same package.
  1. scala> class Class1 {
  2.      | protected def pMethod = "protected"
  3.      | }
  4. defined class Class1
  5. scala> class Class2 { 
  6. // pMethod is not accessible because Class2 is not a subclass of Class1
  7.      | new Class1().pMethod 
  8.      | }
  9. <console>:6: error: method pMethod cannot be accessed in Class1
  10.        new Class1().pMethod
  11.                     ^
  12. scala> class Class3 extends Class1 {
  13. // also unlike Java, protected restricts to the same object
  14.      | new Class1().pMethod
  15.      | }
  16. <console>:6: error: method pMethod cannot be accessed in Class1
  17.        new Class1().pMethod
  18.                     ^
  19. scala> class Class3 extends Class1 {
  20.      | pMethod
  21.      | }
  22. defined class Class3

If the protected is parameterized then only classes that fall into the parameter category can access the parameter:
  1. scala> class Class1 {                                
  2. // protected[Class1] is equivalent to private
  3.      | protected[Class1] def method() = println("hi")
  4.      | method()
  5.      | }
  6. defined class Class1
  7. scala> new Class1()
  8. hi
  9. res0: Class1 = Class1@dc44a6d
  10. // this does not work because method is only accessible in Class1
  11. scala> class Class2 extends Class1 { method() }      
  12. <console>:5: error: not found: value method
  13.        class Class2 extends Class1 { method() }
  14.                                      ^
  15. scala> object Module {                                         
  16.      |   object Inner1 {                                         
  17.      |     protected[Inner1] def innerMethod = println("hi")       
  18.      |     protected[Module] def moduleMethod = println("moduleMethod")
  19.      |
  20.      |     object InnerInner { 
  21.      |       // InnerInner is within Inner1 so the access works
  22.      |       def callInner = innerMethod
  23.      |     }
  24.      |   }
  25.      |   // module method is protected[Module] so anything in Module can access it
  26.      |   def callModuleMethod = Inner1.moduleMethod
  27.      |
  28.      |   object Inner2 {
  29.      |     // Inner1.module method is protected[Module] and 
  30.      |     // Inner2 is within module so therefore has access
  31.      |     def callModuleMethod = Inner1.moduleMethod
  32.      |   }
  33.      | }
  34. defined module Module
  35. scala> Module.callModuleMethod
  36. moduleMethod
  37. scala> Module.Inner1.InnerInner.callInner
  38. hi
  39. scala> Module.Inner1.innerMethod         
  40. <console>:6: error: method innerMethod cannot be accessed in object Module.Inner1
  41.        Module.Inner1.innerMethod
  42.                      ^
  43. scala> Module.Inner1.moduleMethod
  44. <console>:6: error: method moduleMethod cannot be accessed in object Module.Inner1
  45.        Module.Inner1.moduleMethod

The following example shows how package access works in Scala 2.8. They have to be compiled as 2 files.

Root.scala:
  1. package root
  2. class Class1 {
  3.   protected[root] def rootMethod = println("rootMethod")
  4. }
  5. class Class2 {
  6.   // same package so this is legal
  7.   new Class1().rootMethod
  8. }


Subpackage.scala
  1. package root.sub
  2. class Class3 {
  3.   // Class3 is in a subpackage of root so 
  4.   // it can access all objects protected by
  5.   // protected[root] as well as objects
  6.   // protected by protected[root.sub]
  7.   new root.Class1().rootMethod
  8. }

Thursday, October 29, 2009

Boolean Extractors

As discussed in other topics there are several ways to create custom extractors for objects. See
There is one more custom extractor that can be defined. Simple boolean extractors. They are used as follows:
  1. scala>"hello world"match {                   
  2.      | case HasVowels() => println("Vowel found")
  3.      | case _ => println("No Vowels")            
  4.      | }
  5. Vowel found

A boolean extractor is an object that returns Boolean from the unapply method rather than Option[_].

Examples:
  1. scala>object HasVowels{ defunapply(in:String):Boolean = in.exists( "aeiou" contains _ ) }
  2. defined module HasVowels
  3. // Note that HasVowels() has ().
  4. // This is critical because otherwise the match checks whether
  5. // the input is the HasVowels object.
  6. // The () forces the unapply method to be used for matching
  7. scala>"hello world"match {
  8.      | case HasVowels() => println("Vowel found")
  9.      | case _ => println("No Vowels")
  10.      | }
  11. Vowel found
  12. // Don't forget the ()
  13. scala>"kkkkkk"match {    
  14.      | case HasVowels() => println("Vowel found")
  15.      | case _ => println("No Vowels")
  16.      | }
  17. No Vowels
  18. scala>class HasChar(c:Char) {           
  19.      | defunapply(in:String) = in.contains(c)
  20.      | }
  21. defined class HasChar
  22. scala>val HasC = new HasChar('c')
  23. HasC: HasChar = HasChar@3f2f529b
  24. // Don't forget the () it is required here as well
  25. scala>"It actually works!"match {
  26.      | case HasC() => println("a c was found")
  27.      | case _ => println("no c found")  
  28.      | }
  29. a c was found
  30. // Don't forget the ()
  31. scala>"hello world"match { 
  32.      | case HasC() => println("a c was found")
  33.      | case _ => println("no c found")       
  34.      | }
  35. no c found

Monday, September 28, 2009

Extractors 1 (Unapply)

When defining a match such as case Tuple2(one, two) the methods Tuple2.unapply and Tuple2.unapplySeq are called to see if that case can match the input. If one of methods return a Some(...) object then the case is considered to be a match. These methods are called Extractor methods because they essentially decompose the object into several parameters.

I will cover unapplySeq later.

Examples are the best way to illustrate the issue:
  1. // The unapply method of this object takes a string and returns an Option[String]
  2. //   This means that the value being matched must be a string and the value extracted is also a string
  3. scala>object SingleParamExtractor {
  4.      | defunapply(v:String):Option[String] = if(v.contains("Hello")) Some("Hello") else None
  5.      | }
  6. defined module SingleParamExtractor
  7. // this Will match since the extractor will return a Some object
  8. scala>"Hello World"match { case SingleParamExtractor(v) => println(v) }
  9. Hello
  10. // this will not match and therefore an exception will be thrown
  11. scala>"Hi World"match { case SingleParamExtractor(v) => println(v) }   
  12. scala.MatchError: Hi World
  13.                   at .(:7)
  14.                   at .()
  15.                   at RequestResult$.(:3)
  16.                   at RequestResult$.()
  17.                   at RequestResult$result()
  18.                   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  19.                   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  20.                   at sun.reflect.DelegatingMethodAccessorImpl.invok...
  21. // This extractor converts the string to an int if possible.
  22. scala>object ConvertToInt{         
  23.      | defunapply(v:String):Option[Int] = try{ Some(v.toInt) } catch { case _ => None }
  24.      | }
  25. defined module ConvertToInt
  26. scala>"10"match { case ConvertToInt(i) => println(i)}
  27. 10
  28. // If you want to extract multiple elements you return an Option that contains a Tuple.  
  29. //   In this example we divide the string into two parts if it has a space
  30. scala>object MultipleParamExtractor {                                       
  31.      |  defunapply(v:String):Option[(String,String)] = (v indexOf ' ') match {           
  32.      | case x if (x>0) => Some ((v take x, v drop x+1))
  33.      | case _ => None
  34.      | }
  35.      | }
  36. defined module MultipleParamExtractor
  37. scala>"hello everyone :)"match { case MultipleParamExtractor(one, two) => println(one,two) }
  38. (hello,everyone :))
  39. // Any object with a unapply method can be used it does not have to be defined as an object
  40. // So if you have a class of extractors that needs to be parameterized you can 
  41. // create a class and use instances of that class for matching
  42. scala>class Splitter(sep:Char){
  43.      | defunapply(v:String):Option[(String,String)] = (v indexOf sep) match {
  44.      | case x if (x>0) => Some ((v take x, v drop x+1))
  45.      | case _ => None
  46.      | }
  47.      | }
  48. defined class Splitter
  49. // Remember that we need the matching object start with an uppercase
  50. // See http://daily-scala.blogspot.com/2009/09/case-sensitive-matching.html 
  51. // for details
  52. scala>val SplitOnComma = new Splitter (',')
  53. SplitOnComma: Splitter = Splitter@15eb9b0d
  54. // How cool now can create splitters for all sorts of things
  55. scala>"1,2"match { case SplitOnComma(one,two) => println(one,two)}
  56. (1,2)
  57. // All extractors can also be used in assignments
  58. scala>val SplitOnComma(one,two) = "1,2"                           
  59. one: String = 1
  60. two: String = 2

Monday, September 14, 2009

Factory Methods and Companion Objects

One of the most common uses of a companion object (See Companion Objects for more) is as a factory for creating instances of the class. For example, there may be several overloaded apply methods which provide different ways of instantiating the object. This is often preferred to adding many constructors because Scala places restrictions on constructors that Java does not have.

One built in example of Factory methods in a companion object are when you create a case class.

Examples:
  1. scala> caseclass Data(p1:Int, p2:String)
  2. defined class Data
  3. // This is using the generated (or synthetic) factory method.
  4. // call case classes have a factory method generated
  5. scala> Data(1,"one")
  6. res0: Data = Data(1,one)
  7. // This is the normal new syntax.
  8. // case-classes are normal object so they have one of these too
  9. scala> new Data(1,"one")
  10. res1: Data = Data(1,one)
  11. scala> class MyClass(val p1:Int, val p2:String)
  12. defined class MyClass
  13. // MyClass is a normal class so there is no
  14. // synthetic factory method
  15. scala> MyClass(1,"one")
  16. :5: error: not found: value MyClass
  17.        MyClass(1,"one")
  18.        ^
  19. // but of course you can create an instance normally
  20. scala> new MyClass(1,"one")
  21. res3: MyClass = MyClass@55444319
  22. // create the companion object with an apply factory method
  23. scala> object MyClass{
  24.      | def apply(p1:Int, p2:String)=new MyClass(p1,p2)
  25.      | }
  26. defined module MyClass
  27. // now you can create MyClass as if it was a case-class
  28. // It is not a case-class so you still don't have the other
  29. // synthetic methods like: equals, hash-code, toString, etc...
  30. scala> MyClass(1,"one")
  31. res4: MyClass = MyClass@2c5e5c15

Wednesday, September 9, 2009

Using objects to access trait functionality

Today's topic is based on an article by Bill Venners. http://www.artima.com/scalazine/articles/selfless_trait_pattern.html. I recommend reading that article as it goes into much more detail. I also recommend taking a look at the earlier topic that covers companion objects.

The normal way to use a trait is to mix it in to an object. However there can be a problem mixing two traits containing methods with equal signatures. If the two traits are not designed to work together then you will get a compile error. Otherwise one method will override the other. Either way you cannot access both methods. There is an additional way to access the functionality of a trait. You can create an object (not instance) that extends the trait and import the methods when you need them.

If the trait is stateless then the object can be shared if not then make sure that sharing the object is carefully handled.

Examples:
  1. scala> trait T1 {
  2.      | def talk = "hi"
  3.      | }
  4. defined trait T1
  5. scala> trait T2 {
  6.      | def talk = "hello"
  7.      | }
  8. defined trait T2
  9. // Cannot extend C with T1 and T2 because they are not designed to work together
  10. scala> class C extends T1 with T2
  11. :6: error: error overriding method talk in trait T1 of type => java.lang.String;
  12.  method talk in trait T2 of type => java.lang.String needs override modifier
  13.        class C extends T1 with T2
  14.              ^
  15. scala> class C extends T1
  16. defined class C
  17. // objects can have state so becareful how you share them
  18. scala> object Obj1 extends T1
  19. defined module Obj1
  20. scala> object Obj2 extends T2
  21. defined module Obj2
  22. // You can give aliases to the imported methods and use them in the class
  23. scala> class C {
  24.      | import Obj1.{talk => hi}
  25.      | import Obj2.{talk => hello}
  26.      | def sayHi = hi
  27.      | def sayHello = hello
  28.      | }
  29. defined class C
  30. scala> val c = new C
  31. c: C = C@54d8fd1a
  32. scala> c.sayHi
  33. res0: java.lang.String = hi
  34. scala> c.sayHello
  35. res1: java.lang.String = hello
  36. scala> class C extends T1 {
  37.      | import Obj2.{talk => hello}
  38.      | def helloTalk = hello
  39.      | }
  40. defined class C
  41. scala> val c2 = new C
  42. c2: C = C@2ee634bf
  43. scala> c2.talk
  44. res2: java.lang.String = hi
  45. scala> c2.helloTalk
  46. res5: java.lang.String = hello

Companion Object

A companion object is an object with the same name as a class or trait and is defined in the same source file as the associated file or trait. A companion object differs from other objects as it has access rights to the class/trait that other objects do not. In particular it can access methods and fields that are private in the class/trait.

An analog to a companion object in Java is having a class with static methods. In Scala you would move the static methods to a Companion object.

One of the most common uses of a companion object is to define factory methods for class. An example is case-classes. When a case-class is declared a companion object is created for the case-class with a factory method that has the same signature as the primary constructor of the case class. That is why one can create a case-class like: MyCaseClass(param1, param2). No new element is required for case-class instantiation.

A second common use-case for companion objects is to create extractors for the class. I will mention extractors in a future topic. Basically extractors allow matching to work with arbitrary classes.

NOTE: Because the companion object and the class must be defined in the same source file you cannot create them in the interpreter. So copy the following example into a file and run it in script mode:

scala mysourcefile.scala


Example:

  1. class MyString(val jString:String) {
  2.   privatevar extraData = ""
  3.   overridedef toString = jString+extraData
  4. }
  5. object MyString {
  6.   def apply(base:String, extras:String) = {
  7.     val s = new MyString(base)
  8.     s.extraData = extras
  9.     s
  10.   }
  11.   def apply(base:String) = new MyString(base)
  12. }
  13. println(MyString("hello"," world"))
  14. println(MyString("hello"))

Sunday, August 23, 2009

Enumerations

Scala does not have a enum keyword like java so enumerations are not quite as smooth. Depending on your requirements there are two ways to make enumerations.

  1. Create an object that extends the Enumeration class
  2. Create a case-object hierarchy.
I present both ways. Here are some tips:
  • If you only need discrete and related values without custom behaviour create and object that extends Enumeration
  • If each value has custom information associated with it use case-objects

In the following examples notice the use of the sealed keyword when defining the abstract class MyEnum. Sealed specifies that the heirarchy is sealed. Only classes defined in the same file can extend the class.

Also notice in the case object example that the enumeration values are "case object" not "case class". The two are similar except that there is only one instance of a case object. However all the same boiler plate code is generated and you can still match in the same way.

  1. // Note: MyEnum is an object NOT a class
  2. scala>object MyEnum extends Enumeration("one", "two", "three") {
  3.      | type MyEnumType = Value
  4.      | val One, Two, Three = Value
  5.      | }
  6. defined module MyEnum
  7. scala> MyEnum.One
  8. res1: MyEnum.Value = one
  9. scala>def printEnum( value:MyEnum.MyEnumType ) = println( value.toString )
  10. printEnum: (MyEnum.MyEnumType)Unit
  11. scala> printEnum(MyEnum.Two)
  12. two
  13. // If you don't want to prefix enums with MyEnum. Then you
  14. // can import the values.  This is similar to static imports in java
  15. scala>import MyEnum._
  16. import MyEnum._
  17. scala>def printEnum( value:MyEnumType ) = println( value.toString )
  18. printEnum: (MyEnum.MyEnumType)Unit
  19. scala> printEnum(Three)
  20. three
  21. // Similar but with cases objects
  22. // Notice MyEnum is 'sealed' and the parameters have the 'val' keyword so they are public
  23. scala> abstract sealed class MyEnum(val name:String, val someNum:Int)
  24. defined class MyEnum
  25. scala>caseobject One extends MyEnum("one", 1)
  26. defined module One
  27. scala>caseobject Two extends MyEnum("two", 2)
  28. defined module Two
  29. scala>caseobject Three extends MyEnum("three", 3)
  30. defined module Three
  31. scala>def printEnum(value:MyEnum) = println(value.name, value.someNum)
  32. printEnum: (MyEnum)Unit
  33. scala> printEnum(One)
  34. (one,1)

Thursday, August 20, 2009

Object

Scala does not have static methods in the same way that Java does. As a replacement Scala has "objects" and object is a singleton object whose methods can be called in the same manner one would call a static method in Java. The big differentiator is that objects are complete objects and can extent abstract classes and traits.

Objects are sometime referred to as modules as well. See next section for more on modules. In addition there is a special situation where a class has what is called a companion object. That is a topic for another day. Finally you can have case objects, also a topic for another day. case objects will be address as part of the Enumeration topic.
  1. scala> abstract class SuperClass {
  2.     | def method = "hi"
  3.     | val value = 10
  4.     | }
  5. defined class SuperClass
  6. scala>object MyObject extends SuperClass {
  7.     | overridedef method = "Hello"
  8.     | def anotherMethod = "other"
  9.     | }
  10. defined module MyObject
  11. scala> MyObject.method
  12. res0: java.lang.String = Hello
  13. scala> MyObject.value
  14. res1: Int = 10
  15. scala> MyObject.anotherMethod
  16. res2: java.lang.String = other

Objects are also a good way to modularize projects. You can define classes and other objects within an object
  1. scala>object Outer {
  2.     | caseclass Data(name:String)
  3.     |
  4.     | def print(data:Data) = Console.println(data.name)
  5.     |
  6.     | object Factory {
  7.     | def defaultData = Data("defaultName")
  8.     | }
  9.     | }
  10. defined module Outer
  11. scala>val data = Outer.Factory.defaultData
  12. data: Outer.Data = Data(defaultName)
  13. scala> Outer.print(data)
  14. defaultName

Wednesday, August 19, 2009

Implicit Methods

Scala is designed to be a scalable language and one if the features that assist in realizing this goal are implicit methods. I have seen it occasionally referred to as static duck typing (although I personally think that describes structural typing better).

The idea is to be able to extend an existing class with new methods in a type safe manner. Essentially what happens is a developer defines an implicit method (or imports an implicit method) which converts one object from one type to another type. Once the implicit method is within scope all methods are available from both types.

Note: The conversion is one way. If you want bi-directional conversion you need 2 methods.
  1. scala>class MyInteger( i:Int ) {
  2.      | def myNewMethod = println("hello from myNewMethod")
  3.      | }
  4. defined class MyInteger
  5. // an integer is not a "MyInteger" so you cannot call the myNewMethod on it
  6. scala> 1.myNewMethod
  7. :5: error: value myNewMethod is not a member of Int
  8.        1.myNewMethod
  9.          ^
  10. // define an implicit conversion and now we can call all MyInteger methods on integers
  11. scala> implicit def int2MyInt( i:Int ) = new MyInteger(i)
  12. int2MyInt: (Int)MyInteger
  13. scala> 1.myNewMethod
  14. hello from myNewMethod
  15. scala>class MyInteger2( val i:Int ) {
  16.      | def inc = new MyInteger2( i+1 )
  17.      | }
  18. defined class MyInteger2
  19. // you can define several implicits in an object and import later
  20. scala>object Conversions{
  21.      | implicit def int2MyInt2(i:Int) = new MyInteger2(i)
  22.      | implicit def myInt2Int( mI:MyInteger2) = mI.i
  23.      | }
  24. defined module Conversions
  25. // you can import all implicits    or just    one (although interpreter only allows all for some reason)
  26. scala>import Conversions._
  27. import Conversions._
  28. // inc returns a MyInteger2 instance
  29. scala>val j = 1.inc
  30. j: MyInteger2 = MyInteger2@37f808e6
  31. // test    the bi-directionality of it all    so create a method
  32. // that takes and int
  33. scala>def takesInt( i:Int ) = println(i)
  34. takesInt: (Int)Unit
  35. // j is a MyInteger2 but thanks to the implicit it can be converted back to an int
  36. scala> takesInt( j )
  37. 2
  38. scala> takesInt ( 5.inc )
  39. 6
Subscribe to: Comments (Atom)

AltStyle によって変換されたページ (->オリジナル) /