ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 02.01.2026
Просмотров: 408
Скачиваний: 0
Scala for the Impatient
Copyright © Cay S. Horstmann 2012. All Rights Reserved.
The evolution of Java and C++ has slowed down considerably, and programmers who are eager to use more modern language features are looking elsewhere. Scala is an attractive choice; in fact, I think it is by far the most attractive choice for programmers who want to move beyond Java or C++. Scala has a concise syntax that is refreshing after the Java boilerplate. It runs on the Java virtual machine, providing access to a huge set of libraries and tools. It embraces the functional programming style without abandoning object-orientation, giving you an incremental learning path to a new paradigm. The Scala interpreter lets you run quick experiments, which makes learning Scala very enjoyable. And, last but not least, Scala is statically typed, enabling the compiler to find errors, so that you don't waste time finding them later in running programs (or worse, don't find them).
I wrote this book for impatient readers who want to start programming with Scala right away. I assume you know Java, C#, or C++, and I won't bore you with explaining variables, loops, or classes. I won't exhaustively list all features of the language, I won't lecture you about the superiority of one paradigm over another, and I won't make you suffer through long and contrived examples. Instead, you will get the information that you need in compact chunks that you can read and review as needed.
Scala is a big language, but you can use it effectively without knowing all of its details intimately. Martin Odersky, the creator of Scala, has identified the following levels of expertise for application programmers and library designers:
Application Programmer Library Designer Overall Scala Level
Beginning (A1) |
|
|
|
Beginning |
|
|
|
|
|
Intermediate (A2) |
|
Junior (L1) |
|
Intermediate |
|
|
|
|
|
Expert (A3) |
|
Senior (L2) |
|
Advanced |
|
|
|
|
|
|
|
Expert (L3) |
|
Expert |
For each chapter (and occasionally for individual sections), I indicate the experience level. The chapters progress through levels A1, L1, A2, L2, A3, L3. Even if you don't want to design your own libraries, knowing about the tools that Scala provides for library designers can make you a more effective library user.
I hope you enjoy learning Scala with this book. If you find errors or have suggestions for improvement, please visit http://horstmann.com/scala and leave a comment. On that page, you will also find a link to an archive file containing all code examples from the book.
I am very grateful to Dmitry Kirsanov and Alina Kirsanova who turned my manuscript from XHTML into a beautiful book, allowing me to concentrate on the content instead of fussing with the format. Every author should have it so good!
Reviewers include Adrian Cumiskey (Agile Owl Software), Michael Davis (Collaborative Consulting), Daniel Sobral, Craig Tataryn, David Walend, and William Wheeler. Thanks so much for your comments and suggestions!
Finally, as always, my gratitude goes to my editor, Greg Doench, for encouraging me to write this book and for his insights during the development process.
Cay Horstmann
San Francisco, 2012
The Basics
Topics in This Chapter A1
1.1The Scala Interpreter — page 3
1.2Declaring Values and Variables — page 5
1.3Commonly Used Types — page 6
1.4Arithmetic and Operator Overloading — page 7
1.5Calling Functions and Methods — page 9
1.6The apply Method — page 10
1.7Scaladoc — page 10
Exercises — page 13
Chapter 1
In this chapter, you will learn how to use Scala as an industrial-strength pocket calculator, working interactively with numbers and arithmetic operations. We introduce a number of important Scala concepts and idioms along the way. You will also learn how to browse the Scaladoc documentation at a beginner’s level.
Highlights of this introduction are:
•Using the Scala interpreter
•Defining variables with var and val
•Numeric types
•Using operators and functions
•Navigating Scaladoc
1.1 The Scala Interpreter
To start the Scala interpreter:
•Install Scala.
•Make sure that the scala/bin directory is on the PATH.
•Open a command shell in your operating system.
•Type scala followed by the Enter key.
3
4 |
Chapter 1 |
The Basics |
|
TIP: Don’t like the command shell? There are other ways of running the interpreter—see http://horstmann.com/scala/install.
Now type commands followed by Enter. Each time, the interpreter displays the answer. For example, if you type 8 * 5 + 2 (as shown in boldface below), you get 42.
scala> 8 * 5 + 2 res0: Int = 42
The answer is given the name res0. You can use that name in subsequent computations:
scala> 0.5 * res0 res1: Double = 21.0 scala> "Hello, " + res0
res2: java.lang.String = Hello, 42
As you can see, the interpreter also displays the type of the result—in our examples, Int, Double, and java.lang.String.
You can call methods. Depending on how you launched the interpreter, you may be able to use tab completion for method names. Try typing res2.to and then hit the Tab key. If the interpreter offers choices such as
toCharArray |
toLowerCase |
toString |
toUpperCase |
this means tab completion works. Type a U and hit the Tab key again. You now get a single completion:
res2.toUpperCase
Hit the Enter key, and the answer is displayed. (If you can’t use tab completion in your environment, you’ll have to type the complete method name yourself.)
Also try hitting the ↑ and ↓ arrow keys. In most implementations, you will see the previously issued commands, and you can edit them. Use the ←, →, and Del keys to change the last command to
res2.toLowerCase
As you can see, the Scala interpreter reads an expression, evaluates it, prints it, and reads the next expression. This is called the read-eval-print loop, or REPL.
Technically speaking, the scala program is not an interpreter. Behind the scenes, your input is quickly compiled into bytecode, and the bytecode is executed by
1.2 |
|
Declaring Values and Variables |
5 |
|
the Java virtual machine. For that reason, most Scala programmers prefer to call it “the REPL”.
TIP: The REPL is your friend. Instant feedback encourages experimenting, and you will feel good whenever something works.
It is a good idea to keep an editor window open at the same time, so you can copy and paste successful code snippets for later use. Also, as you try more complex examples, you may want to compose them in the editor and then paste them into the REPL.
1.2 Declaring Values and Variables
Instead of using the names res0, res1, and so on, you can define your own names:
scala> val answer = 8 * 5 + 2 answer: Int = 42
You can use these names in subsequent expressions:
scala> 0.5 * answer res3: Double = 21.0
A value declared with val is actually a constant—you can’t change its contents:
scala> answer = 0
<console>:6: error: reassignment to val
To declare a variable whose contents can vary, use a var:
var counter = 0
counter = 1 // OK, can change a var
In Scala, you are encouraged to use a val unless you really need to change the contents. Perhaps surprisingly for Java or C++ programmers, most programs don’t need many var variables.
Note that you need not specify the type of a value or variable. It is inferred from the type of the expression with which you initialize it. (It is an error to declare a value or variable without initializing it.)
However, you can specify the type if necessary. For example,
val greeting: String = null val greeting: Any = "Hello"
6 |
Chapter 1 |
The Basics |
|
NOTE: In Scala, the type of a variable or function is always written after the name of the variable or function. This makes it easier to read declarations with complex types.
As I move back and forth between Scala and Java, I find that my fingers write Java declarations such as String greeting on autopilot, so I have to rewrite them as greeting: String.This is a bit annoying, but when I work with complex Scala programs, I really appreciate that I don’t have to decrypt C-style type declarations.
NOTE: You may have noticed that there were no semicolons after variable declarations or assignments. In Scala, semicolons are only required if you have multiple statements on the same line.
You can declare multiple values or variables together:
val xmax, ymax = 100 // Sets xmax and ymax to 100 var greeting, message: String = null
//greeting and message are both strings, initialized with null
1.3Commonly Used Types
You have already seen some of the data types of the Scala language, such as Int and Double. Like Java, Scala has seven numeric types: Byte, Char, Short, Int, Long, Float, and Double, and a Boolean type. However, unlike Java, these types are classes. There is no distinction between primitive types and class types in Scala. You can invoke methods on numbers, for example:
1.toString() // Yields the string "1"
or, more excitingly,
1.to(10) // Yields Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
(We will discuss the Range class in Chapter 13. For now, just view it as a collection of numbers.)
In Scala, there is no need for wrapper types. It is the job of the Scala compiler to convert between primitive types and wrappers. For example, if you make an array of Int, you get an int[] array in the virtual machine.
As you saw in Section 1.1, “The Scala Interpreter,” on page 3, Scala relies on the underlying java.lang.String class for strings. However, it augments that class with well over a hundred operations in the StringOps class. For example, the intersect method yields the characters that are common to two strings:
1.4 |
|
Arithmetic and Operator Overloading |
7 |
|
"Hello".intersect("World") // Yields "lo"
In this expression, the java.lang.String object "Hello" is implicitly converted to a StringOps object, and then the intersect method of the StringOps class is applied.
Therefore, remember to look into the StringOps class when you use the Scala documentation (see Section 1.7, “Scaladoc,” on page 10).
Similarly, there are classes RichInt, RichDouble, RichChar, and so on. Each of them has a small set of convenience methods for acting on their poor cousins—Int, Double, or Char. The to method that you saw above is actually a method of the RichInt class. In the expression
1.to(10)
the Int value 1 is first converted to a RichInt, and the to method is applied to that value.
Finally, there are classes BigInt and BigDecimal for computations with an arbitrary (but finite) number of digits. These are backed by the java.math.BigInteger and java.math.BigDecimal classes, but, as you will see in the next section, they are much more convenient because you can use them with the usual mathematical operators.
NOTE: In Scala, you use methods, not casts, to convert between numeric types. For example, 99.44.toInt is 99, and 99.toChar is 'c'. Of course, as in Java, the toString method converts any object to a string.
To convert a string containing a number into the number, use toInt or toDouble. For example, "99.44".toDouble is 99.44.
1.4 Arithmetic and Operator Overloading
Arithmetic operators in Scala work just as you would expect in Java or C++:
val answer = 8 * 5 + 2
The + - * / % operators do their usual job, as do the bit operators & | ^ >> <<. There is just one surprising aspect: These operators are actually methods. For example,
a + b
is a shorthand for
a.+(b)
Here, + is the name of the method. Scala has no silly prejudice against nonalphanumeric characters in method names. You can define methods with just
8 |
Chapter 1 |
The Basics |
|
about any symbols for names. For example, the BigInt class defines a method called /% that returns a pair containing the quotient and remainder of a division.
In general, you can write
a method b
as a shorthand for
a.method(b)
where method is a method with two parameters (one implicit, one explicit). For example, instead of
1.to(10)
you can write
1 to 10
Use whatever you think is easier to read. Beginning Scala programmers tend to stick to the Java syntax, and that is just fine. Of course, even the most hardened Java programmers seem to prefer a + b over a.+(b).
There is one notable difference between Scala and Java or C++. Scala does not have ++ or -- operators. Instead, simply use +=1 or -=1:
counter+=1 // Increments counter—Scala has no ++
Some people wonder if there is any deep reason for Scala’s refusal to provide a ++ operator. (Note that you can’t simply implement a method called ++. Since the Int class is immutable, such a method cannot change an integer value.) The Scala designers decided it wasn’t worth having yet another special rule just to save one keystroke.
You can use the usual mathematical operators with BigInt and BigDecimal objects:
val x: BigInt = 1234567890
x * x * x // Yields 1881676371789154860897069000
That’s much better than Java, where you would have had to call x.multiply(x).multiply(x).
NOTE: In Java, you cannot overload operators, and the Java designers claimed this is a good thing because it stops you from inventing crazy operators like !@$&* that would make your program impossible to read. Of course, that’s silly; you can make your programs just as hard to read by using crazy method names like qxywz. Scala allows you to define operators, leaving it up to you to use this feature with restraint and good taste.
1.5 |
|
Calling Functions and Methods |
9 |
|
1.5 Calling Functions and Methods
Scala has functions in addition to methods. It is simpler to use mathematical functions such as min or pow in Scala than in Java—you need not call static methods from a class.
sqrt(2) // Yields 1.4142135623730951 pow(2, 4) // Yields 16.0
min(3, Pi) // Yields 3.0
The mathematical functions are defined in the scala.math package. You can import them with the statement
import scala.math._ // In Scala, the _ character is a “wildcard,” like * in Java
NOTE: To use a package that starts with scala., you can omit the scala prefix. For example, import math._ is equivalent to import scala.math._, and math.sqrt(2) is the same as scala.math.sqrt(2).
We discuss the import statement in more detail in Chapter 7. For now, just use import packageName._ whenever you need to import a particular package.
Scala doesn’t have static methods, but it has a similar feature, called singleton objects, which we will discuss in detail in Chapter 6. Often, a class has a companion object whose methods act just like static methods do in Java. For example, the BigInt companion object to the BigInt class has a method probablePrime that generates a random prime number with a given number of bits:
BigInt.probablePrime(100, scala.util.Random)
Try this in the REPL; you’ll get a number such as 1039447980491200275486540240713. Note that the call BigInt.probablePrime is similar to a static method call in Java.
NOTE: Here, Random is a singleton random number generator object, defined in the scala.util package.This is one of the few situations where a singleton object is better than a class. In Java, it is a common error to construct a new java.util.Random object for each random number.
Scala methods without parameters often don’t use parentheses. For example, the API of the StringOps class shows a method distinct, without (), to get the distinct letters in a string. You call it as
"Hello".distinct
The rule of thumb is that a parameterless method that doesn’t modify the object has no parentheses. We discuss this further in Chapter 5.