Code Examples for

Programming in Scala, Fourth Edition

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 composition-and-inheritance/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 composition-and-inheritance/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 composition-and-inheritance/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 composition-and-inheritance/Ex2.scala Array(1, 2, 3).toString "abc".length
// In file composition-and-inheritance/Ex2.scala "hello".length // no () because no side-effect println() // better to not drop the ()

10.4 Extending classes


// In file composition-and-inheritance/Ex11.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@5ae66c98 scala> ae.width res0: Int = 5
// In file composition-and-inheritance/Ex2.scala val e: Element = new ArrayElement(Array("hello"))

10.5 Overriding methods and fields


// In file composition-and-inheritance/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 composition-and-inheritance/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 composition-and-inheritance/Ex9.scala class ArrayElement( val contents: Array[String] ) extends Element
class ArrayElement(x123: Array[String]) extends Element { val contents: Array[String] = x123 }
// In file composition-and-inheritance/Ex8.scala class Cat { val dangerous = false } class Tiger( override val dangerous: Boolean, private var age: Int ) extends Cat
// In file composition-and-inheritance/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 composition-and-inheritance/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 composition-and-inheritance/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.fill(height)(line) }
// In file composition-and-inheritance/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 composition-and-inheritance/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 composition-and-inheritance/Ex10.scala def above(that: Element): Element = new ArrayElement(this.contents ++ that.contents)
// In file composition-and-inheritance/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 composition-and-inheritance/Ex10.scala new ArrayElement( for ( (line1, line2) <- this.contents zip that.contents ) yield line1 + line2 )
// In file composition-and-inheritance/Ex10.scala Array(1, 2, 3) zip Array("a", "b")
// In file composition-and-inheritance/Ex10.scala Array((1, "a"), (2, "b"))
// In file composition-and-inheritance/Ex10.scala override def toString = contents mkString "\n"
// In file composition-and-inheritance/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 composition-and-inheritance/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 composition-and-inheritance/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" }

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 composition-and-inheritance/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.fill(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) }
// In file composition-and-inheritance/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) val 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) val 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 composition-and-inheritance/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, Fourth Edition (the "Stairway Book"), please visit:

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

and:

http://booksites.artima.com/programming_in_scala_4ed

Copyright © 2007-2019 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.