ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 02.01.2026

Просмотров: 3435

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Section 10.14

Chapter 10 · Composition and Inheritance

246

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)

}

Listing 10.12 · Hiding implementation with private classes.

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index

Section 10.14

Chapter 10 · Composition and Inheritance

247

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"

}

Listing 10.13 · Element with widen and heighten methods.

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index



Section 10.15

Chapter 10 · Composition and Inheritance

248

10.15Putting it all together

A fun way to exercise almost all elements of the layout library is to write a program that draws a spiral with a given number of edges. This Spiral program, shown in Listing 10.14, will do just that:

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))

}

}

Listing 10.14 · The Spiral application.

Because Spiral is a standalone object with a main method with the proper signature, it is a Scala application. Spiral takes one command-line

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index

Section 10.16

Chapter 10 · Composition and Inheritance

249

argument, an integer, and draws a spiral with the specified number of edges. For example, you could draw a six-edge spiral as shown below on the left, and larger spirals as shown to the right:

$ scala Spiral 6

$ scala Spiral 11

$ scala Spiral

17

+-----

+----------

 

+----------------

 

 

|

|

 

|

 

 

| +-+

| +------

+

| +------------

 

+

| + |

| |

|

| |

 

|

| |

| | --+ + |

| | +--------

+ |

+---+

| | | | |

| | |

| |

 

| | ++ | |

| | | +----

+ | |

 

| |

| |

| | | |

| | |

 

| +----

+ |

| | | | ++ | | |

 

|

|

| | | |

| | |

|

 

+--------

+

| | | +--

+ | |

|

 

 

 

| | |

| |

|

 

 

 

| | +------

+ |

|

 

 

 

| |

|

|

 

 

 

| +----------

+

|

 

 

 

|

 

|

 

 

 

+--------------

 

+

10.16Conclusion

In this section, you saw more concepts related to object-oriented programming in Scala. Among others, you encountered abstract classes, inheritance and subtyping, class hierarchies, parametric fields, and method overriding. You should have developed a feel for constructing a non-trivial class hierarchy in Scala. We’ll work with the layout library again in Chapter 14.

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index


Chapter 11

Scala’s Hierarchy

Now that you’ve seen the details of class inheritance in the previous chapter, it is a good time to take a step back and look at Scala’s class hierarchy as a whole. In Scala, every class inherits from a common superclass named Any. Because every class is a subclass of Any, the methods defined in Any are “universal” methods: they may be invoked on any object. Scala also defines some interesting classes at the bottom of the hierarchy, Null and Nothing, which essentially act as common subclasses. For example, just as Any is a superclass of every other class, Nothing is a subclass of every other class. In this chapter, we’ll give you a tour of Scala’s class hierarchy.

11.1 Scala’s class hierarchy

Figure 11.1 shows an outline of Scala’s class hierarchy. At the top of the hierarchy is class Any, which defines methods that include the following:

final def ==(that: Any): Boolean final def !=(that: Any): Boolean def equals(that: Any): Boolean def ##: Int

def hashCode: Int def toString: String

Because every class inherits from Any, every object in a Scala program can be compared using ==, !=, or equals; hashed using ## or hashCode; and formatted using toString. The equality and inequality methods, == and !=, are declared final in class Any, so they cannot be overridden in subclasses.

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index

Section 11.1

Chapter 11 · Scala’s Hierarchy

251

The == method is essentially the same as equals and != is always the negation of equals.1 So individual classes can tailor what == or != means by overriding the equals method. We’ll show an example later in this chapter.

The root class Any has two subclasses: AnyVal and AnyRef. AnyVal is the parent class of every built-in value class in Scala. There are nine such value classes: Byte, Short, Char, Int, Long, Float, Double, Boolean, and

Unit. The first eight of these correspond to Java’s primitive types, and their values are represented at run time as Java’s primitive values. The instances of these classes are all written as literals in Scala. For example, 42 is an instance of Int, 'x' is an instance of Char, and false an instance of Boolean. You cannot create instances of these classes using new. This is enforced by the “trick” that value classes are all defined to be both abstract and final. So if you were to write:

scala> new Int

you would get:

<console>:5: error: class Int is abstract; cannot be instantiated

new Int

ˆ

The other value class, Unit, corresponds roughly to Java’s void type; it is used as the result type of a method that does not otherwise return an interesting result. Unit has a single instance value, which is written (), as discussed in Section 7.2.

As explained in Chapter 5, the value classes support the usual arithmetic and boolean operators as methods. For instance, Int has methods named + and *, and Boolean has methods named || and &&. Value classes also inherit all methods from class Any. You can test this in the interpreter:

1The only cases where == is does not directly call equals is for Java’s boxed numeric classes such as Integer or Long. In Java, a new Integer(1) does not equal a new Long(1) even though for primitive values 1 == 1L. Since Scala is a more regular language than Java it was necessary correct this discrepancy by special-casing the == method for these classes. Likewise, the ## method provides a Scala version of hashing that is the same as Java’s hashCode, except for boxed numeric types, where it works consistently with ==. For instance new Integer(1) and new Long(1) hash the same with ## even though their Java hashCodes are different.

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index


Section 11.1

Subtype

Implicit Conversion

scala Any

Chapter 11 · Scala’s Hierarchy

 

252

 

java.lang String

 

classes) ...

 

 

 

scala AnyRef «java.lang.Object»

 

 

... (other Java

Scala classes) ...

scala Null

 

 

 

 

 

... (other

 

 

 

 

 

 

scala List

 

 

scala ScalaObject

scala Iterable

scala Seq

 

 

scala Nothing

Class hierarchy of Scala.

scala Unit

scala Boolean

scala Char

 

 

 

Figure 11.1 ·

scala AnyVal

 

 

 

scala Short

scala Byte

 

scala Double

scala Float

scala Long

 

scala Int

 

 

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index