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

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

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

Добавлен: 02.01.2026

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

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

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

84

Chapter 7

Packages and Imports

 

8. What is the effect of

import java._ import javax._

Is this a good idea?

9.Write a program that imports the java.lang.System class, reads the user name from the user.name system property, reads a password from the Console object, and prints a message to the standard error stream if the password is not "secret". Otherwise, print a greeting to the standard output stream. Do not use any other imports, and do not use any qualified names (with dots).

10.Apart from StringBuilder, what other members of java.lang does the scala package override?

Inheritance

Topics in This Chapter A1

8.1Extending a Class — page 87

8.2Overriding Methods — page 88

8.3Type Checks and Casts — page 89

8.4Protected Fields and Methods — page 90

8.5Superclass Construction — page 90

8.6Overriding Fields — page 91

8.7Anonymous Subclasses — page 93

8.8Abstract Classes — page 93

8.9Abstract Fields — page 93

8.10Construction Order and Early Definitions L3 — page 94

8.11The Scala Inheritance Hierarchy — page 96

8.12Object Equality L1 — page 97

Exercises — page 98

Chapter 8

In this chapter, you will learn the most important ways in which inheritance in Scala differs from its counterparts in Java and C++. The highlights are:

The extends and final keywords are as in Java.

You must use override when you override a method.

Only the primary constructor can call the primary superclass constructor.

You can override fields.

In this chapter, we only discuss the case in which a class inherits from another class. See Chapter 10 for inheriting traits—the Scala concept that generalizes Java interfaces.

8.1 Extending a Class

You extend a class in Scala just like you would in Java—with the extends keyword:

class Employee extends Person { var salary: 0.0

...

}

As in Java, you specify fields and methods that are new to the subclass or that override methods in the superclass.

87



88

Chapter 8

Inheritance

 

As in Java, you can declare a class final so that it cannot be extended. Unlike Java, you can also declare individual methods or fields final so that they cannot be overridden. (See Section 8.6, “Overriding Fields,” on page 91 for overriding fields.)

8.2 Overriding Methods

In Scala, you must use the override modifier when you override a method that isn’t abstract. (See Section 8.8, “Abstract Classes,” on page 93 for abstract methods.) For example,

public class Person {

...

override def toString = getClass.getName + "[name=" + name + "]"

}

The override modifier can give useful error messages in a number of common situations, such as:

When you misspell the name of the method that you are overriding

When you accidentally provide a wrong parameter type in the overriding method

When you introduce a new method in a superclass that clashes with a subclass method

NOTE: The last case is an instance of the fragile base class problem, where a change in the superclass cannot be verified without looking at all the subclasses.Suppose programmerAlice defines a Person class, and, unbeknownst to Alice, programmer Bob defines a subclass Student with a method id yielding the student ID. Later, Alice also defines a method id that holds the person’s national ID. When Bob picks up that change, something may break in Bob’s program (but not in Alice’s test cases) since Student objects now return unexpected IDs.

In Java, one is often advised to “solve” this problem by declaring all methods as final unless they are explicitly designed to be overridden. That sounds good in theory, but programmers hate it when they can’t make even the most innocuous changes to a method (such as adding a logging call). That’s why Java eventually introduced an optional @Overrides annotation.

Invoking a superclass method in Scala works exactly like in Java, with the keyword super:

8.3

 

Type Checks and Casts

89

 

public class Employee extends Person {

...

override def toString = super.toString + "[salary=" + salary + "]"

}

The call super.toString invokes the toString method of the superclass—that is, the

Person.toString method.

8.3 Type Checks and Casts

To test whether an object belongs to a given class, use the isInstanceOf method. If the test succeeds, you can use the asInstanceOf method to convert a reference to a subclass reference:

if (p.isInstanceOf[Employee]) {

val s = p.asInstanceOf[Employee] // s has type Employee

...

}

The p.isInstanceOf[Employee] test succeeds if p refers to an object of class Employee or its subclass (such as Manager).

If p is null, then p.isInstanceOf[Employee] returns false and p.asInstanceOf[Employee] returns null.

If p is not an Employee, then p.asInstanceOf[Employee] throws an exception.

If you want to test whether p refers to a Employee object, but not a subclass, use

if (p.getClass == classOf[Employee])

The classOf method is defined in the scala.Predef object that is always imported.

Table 8–1 shows the correspondence between Scala and Java type checks and casts.

Table 8–1 Type Checks and Casts in Scala and Java

Scala

Java

obj.isInstanceOf[Cl]

obj instanceof Cl

obj.asInstanceOf[Cl]

(Cl) obj

classOf[Cl]

Cl.class

However, pattern matching is usually a better alternative to using type checks and casts. For example,


90

Chapter 8

Inheritance

 

p match {

case s: Employee => ... // Process s as a Employee case _ => ... // p wasn’t a Employee

}

See Chapter 14 for more information.

8.4 Protected Fields and Methods

As in Java or C++, you can declare a field or method as protected. Such a member is accessible from any subclass, but not from other locations.

Unlike in Java, a protected member is not visible throughout the package to which the class belongs. (If you want this visibility, you can use a package modifier—see Chapter 7.)

There is also a protected[this] variant that restricts access to the current object, similar to the private[this] variant discussed in Chapter 5.

8.5 Superclass Construction

Recall from Chapter 5 that a class has one primary constructor and any number of auxiliary constructors, and that all auxiliary constructors must start with a call to a preceding auxiliary constructor or the primary constructor.

As a consequence, an auxiliary constructor can never invoke a superclass constructor directly.

The auxiliary constructors of the subclass eventually call the primary constructor of the subclass. Only the primary constructor can call a superclass constructor.

Recall that the primary constructor is intertwined with the class definition. The call to the superclass constructor is similarly intertwined. Here is an example:

class Employee(name: String, age: Int, val salary : Double) extends Person(name, age)

This defines a subclass

class Employee(name: String, age: Int, val salary : Double) extends Person(name, age)

and a primary constructor that calls the superclass constructor

class Employee(name: String, age: Int, val salary : Double) extends Person(name, age)

Intertwining the class and the constructor makes for very concise code. You may find it helpful to think of the primary constructor parameters as parameters of

8.6

 

Overriding Fields

91

 

the class. Here, the Employee class has three parameters: name, age, and salary, two of which it “passes” to the superclass.

In Java, the equivalent code is quite a bit more verbose:

public class Employee extends Person { // Java private double salary;

public Employee(String name, int age, double salary) { super(name, age);

this.salary = salary;

}

}

NOTE: In a Scala constructor, you can never call super(params), as you would in Java, to call the superclass constructor.

A Scala class can extend a Java class. Its primary constructor must invoke one of the constructors of the Java superclass. For example,

class Square(x: Int, y: Int, width: Int) extends java.awt.Rectangle(x, y, width, width)

8.6 Overriding Fields

Recall from Chapter 5 that a field in Scala consists of a private field and accessor/mutator methods. You can override a val (or a parameterless def) with another val field of the same name. The subclass has a private field and a public getter, and the getter overrides the superclass getter (or method).

For example,

class Person(val name: String) {

override def toString = getClass.getName + "[name=" + name + "]"

}

class SecretAgent(codename: String) extends Person(codename) { override val name = "secret" // Don’t want to reveal name . . .

override val toString = "secret" // . . . or class name

}

This example shows the mechanics, but it is rather artificial. A more common case is to override an abstract def with a val, like this:


92

Chapter 8

Inheritance

 

abstract class Person { // See Section 8.8 for abstract classes

def id: Int // Each person has an ID that is computed in some way

...

}

class Student(override val id: Int) extends Person

// A student ID is simply provided in the constructor

Note the following restrictions (see also Table 8–2):

A def can only override another def.

A val can only override another val or a parameterless def.

A var can only override an abstract var (see Section 8.8, “Abstract Classes,” on page 93).

Table 8–2 Overriding val, def, and var

Override val

Override def

Override var

with val

with def

with var

• Subclass has a private

Error

Error

 

field (with the same

 

 

 

name as the superclass

 

 

 

field—that’s OK).

 

 

Getter overrides the

 

 

 

superclass getter.

 

 

• Subclass has a private

Like in Java.

A var can override a

 

field.

 

getter/setter pair.

Getter overrides the

 

Overriding just a getter

 

is an error.

 

superclass method.

 

 

 

 

Error

Error

Only if the superclass

 

 

 

var is abstract (see

 

 

 

Section 8.8).

NOTE: In Chapter 5, I said that it’s OK to use a var because you can always change your mind and reimplement it as a getter/setter pair. However, the programmers extending your class do not have that choice. They cannot override a var with a getter/setter pair. In other words, if you provide a var, all subclasses are stuck with it.