Showing posts with label classes. Show all posts
Showing posts with label classes. Show all posts

Sunday, December 13, 2009

Danger: Shadowed member values

It is possible (although not recommended) to declare a public property in a super class and have a private value or variable in a subclass:
  1. scala> class X (val i:Int)
  2. defined class X
  3. scala> class Y(i:Intextends X(19) {
  4.      | println(i)
  5.      | }
  6. defined class Y
  7. scala> val y = new Y(100)
  8. 100
  9. y: Y = Y@3e55a58f
  10. scala> y.i
  11. res7: Int = 19

This begs the question: how can the subclass access the superclass instance of i? Let me re-iterate. Just because it is possible this is a bad idea. This pattern seems like it is begging to create bugs. But who knows there is probably some reason out there where this is required.

The way to access the X.i value from Y is to declare which value you want using the syntax this:X :
  1. scala> class Y(i:Intextends X(19) {
  2.      | println("Y.i"+i)              
  3.      | println("X.i"+(this:X).i)
  4.      | }
  5. defined class Y
  6. scala> new Y(39)
  7. Y.i39
  8. X.i19
  9. res8: Y = Y@451dfada

Repeat: Watch out! Because I learned this danger while writing this topic:
  1. scala> class Y(i:Intextends X(19) {
  2.      | println("1. "+i)              
  3.      | println("2. "+this.i)         
  4.      | println("3. "+(this:Y).i)     
  5.      | println("4. "+(this:X).i)     
  6.      | }
  7. defined class Y
  8. scala> new Y(44)
  9. 1. 44
  10. 2. 44
  11. 3. 19  // this is created via (this:Y).i !
  12. 4. 19
  13. res0: Y = Y@338bd37a

It seems that the syntax (this:X).i accesses the public accessor for i. The public access is a method call I think so it always obtains the value for the property. Shocking!

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. }

Monday, November 2, 2009

Multiple Constructors

In Scala there is a primary constructor: class MyClass(constructorParam:Any). Unlike Java, that constructor must be called. The question that often arises is, "How can one define multiple constructors?" There is a simple way to do this, however often a factory companion object can be used to remove the need for multiple constructors. Factory Companion Objects are covered in a previous post but I will review the pattern here quickly.

First multiple constructors:
  1. scala> class HelloConstructor(param1:Int, param2:Int) {
  2.      | def this(onlyParam:Int) = this(onlyParam,onlyParam)
  3.      | def this(p1:String, p2:String, p3:String) = this(p1.length, p2.length + p3.length)
  4.      | def this(onlyParam:String) = this(onlyParam.length)
  5.      | }
  6. defined class HelloConstructor

In Java if a constructor calls another constructor that call must be the first statement in the constructor. Scala is the same except that in Scala the primary constructor must be called. Notice that all constructors call this(param1,param2) at some point. In addition any method defined in the class HelloConstructor is not available until after the primary constructor is invoked. The following examples are not valid.
  1. scala> class HelloConstructor(param1:Int, param2:Int) {                                  
  2.      | def x = 1
  3.      | def this() = this(x,3)
  4.      | }
  5. <console>:6: error: not found: value x
  6.        def this() = this(x,3)
  7. scala> class HelloConstructor(param1:Int, param2:Int) {
  8.      | def this() = {
  9.      | println("constructing")  // the REPL won't even let me finish method definition
  10. <console>:3: error: 'this' expected but identifier found.
  11.        println("constructing")
  12.        ^

Factory companion objects can be used to work around these restrictions:
  1. scala> class HelloConstructor(param1:Int, param2:Int)  
  2. defined class HelloConstructor
  3. scala> object HelloConstructor {                             
  4.      | def apply() = {
  5.      | println("constructing object")
  6.      | new HelloConstructor(1,2)
  7.      | }
  8.      | }
  9. defined module HelloConstructor
  10. scala> HelloConstructor()
  11. constructing object
  12. res1: HelloConstructor = HelloConstructor@5b0010ec

Since companion objects can access private members of the class the factory methods can be as powerful as a constructor without the restrictions.

One last idea that is useful when designing classes is Scala 2.8 default arguments:
  1. scala> class HelloConstructor(param1: Int = 1, param2: Int = 2)
  2. defined class HelloConstructor
  3. scala> new HelloConstructor()
  4. res0: HelloConstructor = HelloConstructor@7cd47880
  5. scala> new HelloConstructor(1)
  6. res1: HelloConstructor = HelloConstructor@3834a1c8
  7. scala> new HelloConstructor(param1 = 1)
  8. res2: HelloConstructor = HelloConstructor@3b3e3940
  9. scala> new HelloConstructor(param2 = 1)
  10. res3: HelloConstructor = HelloConstructor@6dee2ea8
  11. scala> new HelloConstructor(3,4)       
  12. res4: HelloConstructor = HelloConstructor@397b6074
  13. scala> new HelloConstructor(param1 = 3, param2=4)
  14. res5: HelloConstructor = HelloConstructor@20272fec

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

Friday, August 28, 2009

Apply-update methods

The apply and update methods have special meaning in Scala. They allow a developer to define semantics like java array access for an arbitrary class. For example:
  1. scala>class Phonebook {
  2.      |  val numbers = scala.collection.mutable.Map[String,String]()
  3.      | def apply(name:String):String = numbers(name)
  4.      | def update(name:String, number:String) = numbers(name) = number
  5.      | }
  6. defined class Phonebook
  7. scala>val book = new Phonebook() 
  8. book: Phonebook = Phonebook@1e406b09
  9. scala> book("jesse") = "123 456 7890"
  10. scala> println (book("jesse"))
  11. 123 456 7890

As you can see you can invoke the apply method in a similar way that [] are used on arrays in Java. When you call 'obj(param)' the call is translated by the compiler to: 'obj.apply(param)'. As such apply can have any number of arguments.

Similarly 'obj(param) = value' is compiled to 'obj.update(param,value)'. Again the update method can have as many arguments as you wish. However only the last argument is treated as a value. So:
  1. scala>class Echo {
  2.      | def update(n:String, n2:String, n3:String ) = println(n,n2,n3)
  3.      | }
  4. defined class Echo
  5. scala>val e = new Echo()
  6. e: Echo = Echo@2fa847df
  7. scala> e("hello", "hi") = "bonjour"
  8. (hello,hi,bonjour)
  9. scala> e("hello") = ("salut","bonjour")
  10. :7: error: wrong number of arguments for method update: (String,String,String)Unit
  11.        e("hello") = ("salut","bonjour")
  12.                   ^

This makes sense because if apply has many arguments representing the key then the same key must work for the update method for assignment. If you have many values to assign try:
  1. scala>class Phonebook {
  2.      |  val numbers = scala.collection.mutable.Map[String, (Int, Int)]()
  3.      | def apply(name:String) = numbers(name)
  4.      | def update(name:String, number:(Int,Int)) = numbers(name) = number
  5.      | }
  6. defined class Phonebook
  7. scala>val book2 = new Phonebook()
  8. book2: Phonebook = Phonebook@7a120cb3
  9. scala> book2("jesse") = (123, 4567890)
  10. scala>val (areaCode, local) = book2("jesse")
  11. areaCode: Int = 123
  12. local: Int = 4567890

Thursday, August 13, 2009

Traits and inheritance

Scala provides two structures for inheritance. Classes (abstract or not) and traits. Traits are very similar to Ruby Mixins meaning that they can contain code like abstract classes but like interfaces multiple traits can be inherited from.
Like interfaces traits cannot have constructors but in Scala variables can be abstract and therefore provide an easy way to simulate a constructor.
There are no method resolution conflicts because method definitions are always resolved right to left:
  1. class X extends Y with A with B with C

If ABC and Y all have the method (doit) the method in C will be used. If C calls super.doit that will call B.doit and so on.
Note: If Y defines doit the A, B, and C must define doit with the override keyword:
  1. override def doit() = {...}

In the above example ABC must be traits but Y can be a class or a trait. When inheriting you must always have one extends keyword which can optionally followed by one or more with clauses.

  1. scala> abstract class Animal {
  2.      |  val legs:Int
  3.      | val noise:String
  4.      | def makeNoise() = println(noise)
  5.      | }
  6. defined class Animal
  7. scala>  trait Quadriped {
  8.      | self:Animal =>
  9.      | val legs = 4
  10.      | }
  11. defined trait Quadriped
  12. scala> trait Biped {
  13.      | self:Animal =>
  14.      | val legs = 2
  15.      | }
  16. defined trait Biped
  17. scala> class Dog extends Animal with Quadriped {
  18.      | val noise = "Woof"
  19.      | override def makeNoise() = println( noise+" "+noise)
  20.      | }
  21. defined class Dog
  22. scala> new Dog().makeNoise()
  23. Woof Woof
  24. scala> abstract class GenericAnimal extends Animal{ 
  25.      | val noise = "glup"                          
  26.      | }
  27. defined class GenericAnimal
  28. scala> val quad = new GenericAnimal() with Quadriped
  29. quad: GenericAnimal with Quadriped = $anon$1@10bfb545
  30. scala> quad.makeNoise()
  31. glup
  32. scala> val biped = new GenericAnimal() with Biped
  33. biped: GenericAnimal with Biped = $anon$1@7669521
  34. scala> val biped = new GenericAnimal() with Biped{
  35.      | override val noise = "Hello"
  36.      | }
  37. biped: GenericAnimal with Biped = $anon$1@6366ce5f
  38. scala> biped.makeNoise()
  39. Hello

Sunday, August 9, 2009

Case classes

Case classes are a special type of class that comes with several convenient definitions (equals, hashCode, toString, copy). There are two main uses:

1. Data object
2. Matching

First examples is using as a datastructure (Note: Some and None are both case classes):
  1. scala>val tree = Node( "root",
  2.      |                  Some(Node( "left", None, None)),
  3.      |                  Some(Node( "right", None, Some( Node( "grandchild", None, None) ) ) )
  4.      |                )
  5. tree: Node = Node(root,Some(Node(left,None,None)),Some(Node(right,None,Some(Node(grandchild,None,None)))))
  6. // you can deconstruct a datastructure made of cases classes
  7. scala> tree match {
  8.      | case Node( _, Some(left), Some(right) ) => println(left, right)
  9.      | case _ => println( "shouldnt happen" )
  10.      | }
  11. (Node(left,None,None),Node(right,None,Some(Node(grandchild,None,None))))
  12. scala>caseclass DataStructure( value1:String, value2:Int, value3:String)
  13. defined class DataStructure
  14. scala>  val d = DataStructure("one", 2, "three")
  15. d: DataStructure = DataStructure(one,2,three)
  16. scala> d.toString
  17. res0: String = DataStructure(one,2,three)
  18. scala> d == DataStructure("one", 2, "three")
  19. res1: Boolean = true
  20. scala> d == DataStructure("zero",1, "two")
  21. res2: Boolean = false
  22. scala> d.hashCode
  23. res4: Int = 1652907895
  24. // Only available in Scala 2.8
  25. scala> d.copy( value3="newValue")
  26. res5: DataStructure = DataStructure(one,2,newValue)

Thursday, August 6, 2009

Simple classes

Scala classes are similar to java classes but are designed to work around some of the boiler plate code. We will address multiple constructors another day. But one thing to note is that normally the code that is in a Java constructor goes directly in the body of a Scala class or in a companion object (also covered later).
  1. scala>class NewClass( aPrivateInt:Int, val iAmPublic:Int, var iAmAlsoPublic:Int)
  2. defined class NewClass
  3. scala>val c = new NewClass(1,2,3)
  4. c: NewClass = NewClass@da99836
  5. scala> c.aPrivateInt
  6. :7: error: value aPrivateInt is not a member of NewClass
  7.        c.aPrivateInt
  8.          ^
  9. scala> c.iAmPublic
  10. res2: Int = 2
  11. scala> c.iAmAlsoPublic
  12. res3: Int = 3
  13. scala> c.iAmPublic = 10 // not allowed because iAmPublic is a val
  14. :6: error: reassignment to val
  15.        c.iAmPublic = 10
  16.                    ^
  17. scala> c.iAmAlsoPublic = 10 // iAmAlsoPublic is a var
  18. scala> c.iAmAlsoPublic
  19. res4: Int = 10
  20. scala>class c2( aPrivate:Int ) {
  21.      | val aPublic = aPrivate + 10
  22.      | }
  23. defined class c2
  24. scala>class c3(tmp:Int) extends c2(tmp){
  25.      | overrideval aPublic = tmp * 10 // can't access super.aPrivate because it
  26.  is classprivate so use tmp
  27.      | }
  28. defined class c3
  29. scala>new c2(10).aPublic
  30. res5: Int = 20
  31. scala>new c3(10).aPublic
  32. res6: Int = 100
  33. scala>class c4( aPrivate:Int ) {
  34.      | println(aPrivate)
  35.      | privatevalprivate2 = aPrivate % 10
  36.      | println(private2)
  37.      | }
  38. defined class c4
  39. scala>new c4(22)
  40. 22
  41. 2
  42. res7: c4 = c4@64d90254