Code Examples for

Programming in Scala, Fifth 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: Vector[String]
abstract class Element ...
scala> new Element 1 |new Element | ^^^^^^^ | Element is abstract; it cannot be instantiated

10.3 Defining parameterless methods


// In file composition-and-inheritance/Ex9.scala abstract class Element: def contents: Vector[String] def height: Int = contents.length def width: Int = if height == 0 then 0 else contents(0).length
def width(): Int
def width: Int
// In file composition-and-inheritance/Ex2.scala abstract class Element: def contents: Vector[String] val height = contents.length val width = if height == 0 then 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/Ex10.scala class VectorElement(conts: Vector[String]) extends Element: def contents: Vector[String] = conts
... extends Element ...
val ve = VectorElement(Vector("hello", "world")) ve.width // 5
// In file composition-and-inheritance/Ex2.scala val e: Element = VectorElement(Vector("hello"))

10.5 Overriding methods and fields


// In file composition-and-inheritance/Ex3.scala class VectorElement(conts: Vector[String]) extends Element: val contents: Vector[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 // Extends Element shown in Listing 10.2 class VectorElement( val contents: Vector[String] ) extends Element
class VectorElement(x123: Vector[String]) extends Element: val contents: Vector[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 // Extends VectorElement shown in Listing 10.5 class LineElement(s: String) extends VectorElement(Vector(s)): override def width = s.length override def height = 1
... extends VectorElement(Vector(s)) ...

10.8 Using override modifiers


$ scalac LineElement.scala -- [E037] Declaration Error: LineElement.scala:3:15 -- 3 | override def hight = 1 | ^ | method hight overrides nothing
def hidden(): Boolean
-- Error: Circle.scala:3:6 --------------------------- 3 | def hidden(): Boolean = | ^ | error overriding method hidden in class Shape | of type (): Boolean; method hidden of type | (): Boolean needs `override` modifier

10.9 Polymorphism and dynamic binding


// In file composition-and-inheritance/Ex9.scala // Extends Element shown in Listing 10.2 class UniformElement( ch: Char, override val width: Int, override val height: Int ) extends Element: private val line = ch.toString * width def contents = Vector.fill(height)(line)
// In file composition-and-inheritance/Ex9.scala val e1: Element = VectorElement(Vector("hello", "world")) val ve: VectorElement = LineElement("hello") val e2: Element = ve val e3: Element = UniformElement('x', 2, 3)
abstract class Element: def demo = "Element's implementation invoked" class VectorElement extends Element: override def demo = "VectorElement's implementation invoked" class LineElement extends VectorElement: override def demo = "LineElement's implementation invoked" // UniformElement inherits Element's demo class UniformElement extends Element
def invokeDemo(e: Element) = e.demo
invokeDemo(new VectorElement) // VectorElement's implementation invoked
invokeDemo(new LineElement) // LineElement's implementation invoked
invokeDemo(new UniformElement) // Element's implementation invoked

10.10 Declaring final members


class VectorElement extends Element: final override def demo = "VectorElement's implementation invoked"
-- Error: LineElement.scala:2:15 ---------------------- 2 | override def demo = | ^ |error overriding method demo in class VectorElement | of type => String; method demo of type => String | cannot override final member method demo in class | VectorElement
final class VectorElement extends Element: override def demo = "VectorElement's implementation invoked"
-- [E093] Syntax Error: LineElement.scala:1:6 --------- 1 |class LineElement extends VectorElement: | ^ | class LineElement cannot extend final class | VectorElement

10.11 Using composition and inheritance


// In file composition-and-inheritance/Ex10.scala class LineElement(s: String) extends Element: val contents = Vector(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 = VectorElement(this.contents ++ that.contents)
// In file composition-and-inheritance/Ex10.scala def beside(that: Element): Element = val newContents = new Array[String](this.contents.length) for i <- 0 until this.contents.length do newContents(i) = this.contents(i) + that.contents(i) VectorElement(newContents.toVector)
// In file composition-and-inheritance/Ex10.scala VectorElement( for (line1, line2) <- this.contents.zip(that.contents) yield line1 + line2)
// In file composition-and-inheritance/Ex10.scala Vector(1, 2, 3).zip(Vector("a", "b"))
// In file composition-and-inheritance/Ex10.scala Vector((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: Vector[String] def width: Int = if height == 0 then 0 else contents(0).length def height: Int = contents.length def above(that: Element): Element = VectorElement(this.contents ++ that.contents) def beside(that: Element): Element = VectorElement( for (line1, line2) <- this.contents.zip(that.contents) yield line1 + line2 ) override def toString = contents.mkString("\n") end Element

10.13 Defining a factory object


// In file composition-and-inheritance/Ex12.scala object Element: def elem(contents: Vector[String]): Element = VectorElement(contents) def elem(chr: Char, width: Int, height: Int): Element = UniformElement(chr, width, height) def elem(line: String): Element = LineElement(line)
// In file composition-and-inheritance/Ex12.scala import Element.elem abstract class Element: def contents: Vector[String] def width: Int = if height == 0 then 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") end Element
// In file composition-and-inheritance/LayoutElement.scala object Element: private class VectorElement( val contents: Vector[String] ) extends Element private class LineElement(s: String) extends Element: val contents = Vector(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 = Vector.fill(height)(line) def elem(contents: Vector[String]): Element = VectorElement(contents) def elem(chr: Char, width: Int, height: Int): Element = UniformElement(chr, width, height) def elem(line: String): Element = LineElement(line) end Element

10.14 Heighten and widen


elem(Vector("hello")) above elem(Vector("world!"))
elem(Vector("one", "two")) beside elem(Vector("one"))
// In file composition-and-inheritance/LayoutElement.scala import Element.elem abstract class Element: def contents: Vector[String] def width: Int = if height == 0 then 0 else 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 then 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 then 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") end Element

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 then 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 then (corner beside horizontalBar) above (sp beside space) else if direction == 1 then (sp above space) beside (corner above verticalBar) else if direction == 2 then (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)) end Spiral
$ scala Spiral 6 $ scala Spiral 11 $ scala Spiral 17 +----- +---------- +---------------- | | | | +-+ | +------+ | +------------+ | + | | | | | | | | | | | +--+ | | | +--------+ | +---+ | | | | | | | | | | | | ++ | | | | | +----+ | | | | | | | | | | | | | | +----+ | | | | | ++ | | | | | | | | | | | | | +--------+ | | | +--+ | | | | | | | | | | | +------+ | | | | | | | +----------+ | | | +--------------+

10.16 Conclusion

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

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

and:

http://booksites.artima.com/programming_in_scala_5ed

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