ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 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