Code Examples for

Programming in Scala

Return to chapter index

10 Composition and Inheritance

Sample run of chapter's interpreter examples

10.1 A two-dimensional layout library


elem(s: String): Element
// In file compo-inherit/LayoutElement.scala val column1 = elem("hello") above elem("***") val column2 = elem("***") above elem("world") column1 beside column2
hello *** *** world

10.2 Abstract classes


abstract class Element { def contents: Array[String] }
abstract class Element ...
scala> new Element <console>:5: error: class Element is abstract; cannot be instantiated new Element ^

10.3 Defining parameterless methods


// In file compo-inherit/Ex9.scala abstract class Element { def contents: Array[String] def height: Int = contents.length def width: Int = if (height == 0) 0 else contents(0).length }
def width(): Int
def width: Int
// In file compo-inherit/Ex2.scala abstract class Element { def contents: Array[String] val height = contents.length val width = if (height == 0) 0 else contents(0).length }
// In file compo-inherit/Ex2.scala Array(1, 2, 3).toString "abc".length
// In file compo-inherit/Ex2.scala "hello".length // no () because no side-effect println() // better to not drop the ()

10.4 Extending classes


// In file compo-inherit/Ex2.scala class ArrayElement(conts: Array[String]) extends Element { def contents: Array[String] = conts }
... extends Element ...
scala> val ae = new ArrayElement(Array("hello", "world")) ae: ArrayElement = ArrayElement@d94e60 scala> ae.width res1: Int = 5
// In file compo-inherit/Ex2.scala val e: Element = new ArrayElement(Array("hello"))

10.5 Overriding methods and fields


// In file compo-inherit/Ex3.scala class ArrayElement(conts: Array[String]) extends Element { val contents: Array[String] = conts }
// This is Java class CompilesFine { private int f = 0; public int f() { return 1; } }
// In file compo-inherit/Ex4.scala.err class WontCompile { private var f = 0 // Won't compile, because a field def f = 1 // and method have the same name }

10.6 Defining parametric fields


// In file compo-inherit/Ex9.scala class ArrayElement( val contents: Array[String] ) extends Element
class ArrayElement(x123: Array[String]) extends Element { val contents: Array[String] = x123 }
// In file compo-inherit/Ex8.scala class Cat { val dangerous = false } class Tiger( override val dangerous: Boolean, private var age: Int ) extends Cat
// In file compo-inherit/Ex8.scala class Tiger(param1: Boolean, param2: Int) extends Cat { override val dangerous = param1 private var age = param2 }

10.7 Invoking superclass constructors


// In file compo-inherit/Ex9.scala class LineElement(s: String) extends ArrayElement(Array(s)) { override def width = s.length override def height = 1 }
... extends ArrayElement(Array(s)) ...

10.8 Using override modifiers


$ scalac LineElement.scala .../LineElement.scala:50: error: method hight overrides nothing override def hight = 1 ^
def hidden(): Boolean
.../Shapes.scala:6: error: error overriding method hidden in class Shape of type ()Boolean; method hidden needs `override' modifier def hidden(): Boolean = ^

10.9 Polymorphism and dynamic binding


// In file compo-inherit/Ex9.scala class UniformElement( ch: Char, override val width: Int, override val height: Int ) extends Element { private val line = ch.toString * width def contents = Array.make(height, line) }
// In file compo-inherit/Ex9.scala val e1: Element = new ArrayElement(Array("hello", "world")) val ae: ArrayElement = new LineElement("hello") val e2: Element = ae val e3: Element = new UniformElement('x', 2, 3)
abstract class Element { def demo() { println("Element's implementation invoked") } } class ArrayElement extends Element { override def demo() { println("ArrayElement's implementation invoked") } } class LineElement extends ArrayElement { override def demo() { println("LineElement's implementation invoked") } } // UniformElement inherits Element's demo class UniformElement extends Element
def invokeDemo(e: Element) { e.demo() }
scala> invokeDemo(new ArrayElement) ArrayElement's implementation invoked
scala> invokeDemo(new LineElement) LineElement's implementation invoked
scala> invokeDemo(new UniformElement) Element's implementation invoked

10.10 Declaring final members


class ArrayElement extends Element { final override def demo() { println("ArrayElement's implementation invoked") } }
elem.scala:18: error: error overriding method demo in class ArrayElement of type ()Unit; method demo cannot override final member override def demo() { ^
final class ArrayElement extends Element { override def demo() { println("ArrayElement's implementation invoked") } }
elem.scala: 18: error: illegal inheritance from final class ArrayElement class LineElement extends ArrayElement { ^

10.11 Using composition and inheritance


// In file compo-inherit/Ex11.scala class LineElement(s: String) extends Element { val contents = Array(s) override def width = s.length override def height = 1 }

10.12 Implementing above, beside, and toString


// In file compo-inherit/Ex10.scala def above(that: Element): Element = new ArrayElement(this.contents ++ that.contents)
// In file compo-inherit/Ex10.scala def beside(that: Element): Element = { val contents = new Array[String](this.contents.length) for (i <- 0 until this.contents.length) contents(i) = this.contents(i) + that.contents(i) new ArrayElement(contents) }
// In file compo-inherit/Ex10.scala new ArrayElement( for ( (line1, line2) <- this.contents zip that.contents ) yield line1 + line2 )
// In file compo-inherit/Ex10.scala Array(1, 2, 3) zip Array("a", "b")
// In file compo-inherit/Ex10.scala Array((1, "a"), (2, "b"))
// In file compo-inherit/Ex10.scala override def toString = contents mkString "\n"
// In file compo-inherit/Ex11.scala abstract class Element { def contents: Array[String] def width: Int = if (height == 0) 0 else contents(0).length def height: Int = contents.length def above(that: Element): Element = new ArrayElement(this.contents ++ that.contents) def beside(that: Element): Element = new ArrayElement( for ( (line1, line2) <- this.contents zip that.contents ) yield line1 + line2 ) override def toString = contents mkString "\n" }

10.13 Defining a factory object


// In file compo-inherit/Ex12.scala object Element { def elem(contents: Array[String]): Element = new ArrayElement(contents) def elem(chr: Char, width: Int, height: Int): Element = new UniformElement(chr, width, height) def elem(line: String): Element = new LineElement(line) }
// In file compo-inherit/Ex12.scala import Element.elem abstract class Element { def contents: Array[String] def width: Int = if (height == 0) 0 else contents(0).length def height: Int = contents.length def above(that: Element): Element = elem(this.contents ++ that.contents) def beside(that: Element): Element = elem( for ( (line1, line2) <- this.contents zip that.contents ) yield line1 + line2 ) override def toString = contents mkString "\n" }
// In file compo-inherit/LayoutElement.scala object Element { private class ArrayElement( val contents: Array[String] ) extends Element private class LineElement(s: String) extends Element { val contents = Array(s) override def width = s.length override def height = 1 } private class UniformElement( ch: Char, override val width: Int, override val height: Int ) extends Element { private val line = ch.toString * width def contents = Array.make(height, line) } def elem(contents: Array[String]): Element = new ArrayElement(contents) def elem(chr: Char, width: Int, height: Int): Element = new UniformElement(chr, width, height) def elem(line: String): Element = new LineElement(line) }

10.14 Heighten and widen


new ArrayElement(Array("hello")) above new ArrayElement(Array("world!"))
new ArrayElement(Array("one", "two")) beside new ArrayElement(Array("one"))
// In file compo-inherit/LayoutElement.scala import Element.elem abstract class Element { def contents: Array[String] def width: Int = contents(0).length def height: Int = contents.length def above(that: Element): Element = { val this1 = this widen that.width val that1 = that widen this.width elem(this1.contents ++ that1.contents) } def beside(that: Element): Element = { val this1 = this heighten that.height val that1 = that heighten this.height elem( for ((line1, line2) <- this1.contents zip that1.contents) yield line1 + line2) } def widen(w: Int): Element = if (w <= width) this else { val left = elem(' ', (w - width) / 2, height) var right = elem(' ', w - width - left.width, height) left beside this beside right } def heighten(h: Int): Element = if (h <= height) this else { val top = elem(' ', width, (h - height) / 2) var bot = elem(' ', width, h - height - top.height) top above this above bot } override def toString = contents mkString "\n" }

10.15 Putting it all together


// In file compo-inherit/Spiral.scala import Element.elem object Spiral { val space = elem(" ") val corner = elem("+") def spiral(nEdges: Int, direction: Int): Element = { if (nEdges == 1) elem("+") else { val sp = spiral(nEdges - 1, (direction + 3) % 4) def verticalBar = elem('|', 1, sp.height) def horizontalBar = elem('-', sp.width, 1) if (direction == 0) (corner beside horizontalBar) above (sp beside space) else if (direction == 1) (sp above space) beside (corner above verticalBar) else if (direction == 2) (space beside sp) above (horizontalBar beside corner) else (verticalBar above corner) beside (space above sp) } } def main(args: Array[String]) { val nSides = args(0).toInt println(spiral(nSides, 0)) } }
$ scala Spiral 6 $ scala Spiral 11 $ scala Spiral 17 +----- +---------- +---------------- | | | | +-+ | +------+ | +------------+ | + | | | | | | | | | | | +--+ | | | +--------+ | +---+ | | | | | | | | | | | | ++ | | | | | +----+ | | | | | | | | | | | | | | +----+ | | | | | ++ | | | | | | | | | | | | | +--------+ | | | +--+ | | | | | | | | | | | +------+ | | | | | | | +----------+ | | | +--------------+

10.16 Conclusion

For more information about Programming in Scala (the "Stairway Book"), please visit:

http://www.artima.com/shop/programming_in_scala

and:

http://booksites.artima.com/programming_in_scala

Copyright © 2007-2008 Artima, Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.