Show
Chapter 2. Working with Data: Literals, Values, Variables, and TypesIn this chapter we will cover the core data and variable types in Scala. Let’s start with the definitions of the terms literal, value, variable, and type:
The data stored in values and variables in Scala will get automatically deallocated by the Java Virtual Machine’s garbage collection when they are no longer used. There is no ability, or need, to deallocate them manually. Let’s try exercising these terms by working with data in the Scala REPL. Scala values are defined with the syntax scala> val x: Int = 5 x: Int = 5 What happened here? The REPL (again, a Read-Evaluate-Print-Loop shell)
read the value definition, evaluated it, and reprinted it as a confirmation. The new value, named scala> x res0: Int = 5 scala> x * 2 res1: Int = 10 scala> x / 5 res2: Int = 1 Each of these three input lines are valid Scala syntax and return an integer value. In each case, because a value is returned, the REPL repeats the value and its type and also assigns a unique, sequentially named value starting with scala> res0 * res1 res3: Int = 50 Here the values Let’s try working with variables now. Variables, which unlike values are mutable and can be reassigned new values, are defined with the syntax Here is an example of working with variables: scala> var a: Double = 2.72 a: Double = 2.72 scala> a = 355.0 / 113.0 a: Double = 3.1415929203539825 scala> a = 5 a: Double = 5.0 In this example we defined the variable
This has been a short introduction to using values, variables, types, and literals in Scala. In the rest of this chapter we will cover each of these subject areas in depth. ValuesValues are immutable, typed storage units, and by convention
are the default method for storing data. You can define a new value using the Syntax: Defining a Value val <identifier>[: <type>] = <data> Values require both a name and assigned data, but they do not require an explicit type. If the type is not specified (i.e., the “: <type>” syntax is not included), the Scala compiler will infer the type based on the assigned data. Here are some examples of defining values with their type in the Scala REPL: scala> val x: Int = 20 x: Int = 20 scala> val greeting: String = "Hello, World" greeting: String = Hello, World scala> val atSymbol: Char = '@' atSymbol: Char = @ You may have noticed from the syntax diagram that specifying the type in value definitions is optional. In situations where it is possible to deduce the type of the value based on its assignment (for example, the literal Let’s try the examples again without specifying their types: scala> val x = 20 x: Int = 20 scala> val greeting = "Hello, World" greeting: String = Hello, World scala> val atSymbol = '@' atSymbol: Char = @ In this example the values end up having the same types ( Using Scala’s type inference is a helpful shortcut when writing code because it removes the need to explicitly write the type of a value. As a guideline it should only be used when it does not reduce the readability of your code. In the case that someone reading your code would not be able to figure out what the type of the value is, it would be better to include the explicit type in the value definition. Although type inference will deduce the correct type to use to store data, it will not override an explicit type that you set. If you define a value with a type that is incompatible with the initial value you will get a compilation error: scala> val x: Int = "Hello" <console>:7: error: type mismatch; found : String("Hello") required: Int val x: Int = "Hello" The error here affirms that an VariablesIn computer science the term variable typically refers to a unique identifier corresponding to an allocated or reserved memory space, into which values can be stored and from which values can be retrieved. As long as the memory space is reserved, it can be assigned new values over and over again. Thus, the contents of the memory space are dynamic, or variable. In most languages, such as C, Java, PHP, Python, and Ruby, this is the typical pattern for working with named, assignable memory storage. Variables are dynamic, mutable, and reassignable (with the exception of those defined with special restrictions such as Java’s In Scala, values are preferred over variables by convention, due to the stability and predictability they bring to source code. When you define a value you can be assured that it will retain the same value regardless of any other code that may access it. Reading and debugging code is easier when a value assigned at the beginning of a code segment is unchanged through the end of the code segment. Finally, when working with data that may be available for the life span of an application, or accessible from concurrent or multithreaded code, an immutable value will be more stable and less prone to errors than mutable data that may be modified at unexpected times. The example code and exercises in this book prefer the use of values over variables. However, in those places where variables are more suitable, such as local variables that store temporary data or accumulate values in loops, variables will certainly be used. Now that the preference for values over variables has been explained in detail, we can put that aside and cover how to use variables in Scala. The Syntax: Defining a Variable var <identifier>[: <type>] = <data> Like values, variables can be defined with or without an explicit type. If no type is specified the Scala compiler will use type inference to determine the correct type to assign to your variable. Unlike values, variables can be reassigned new data at any time. Here is an example of defining a variable and then reassigning it, in this case to the product of itself and another number: scala> var x = 5 x: Int = 5 scala> x = x * 4 x: Int = 20 Although a variable can be reassigned, its designated type cannot, and so a variable cannot be reassigned data that has an incompatible type. For example, defining a variable of type scala> var x = 5 x: Int = 5 scala> x = "what's up?" <console>:8: error: type mismatch; found : String("what\'s up?") required: Int x = "what's up?" ^ However, defining a variable of type scala> var y = 1.5 y: Double = 1.5 scala> y = 42 y: Double = 42.0 NamingScala names can use letters, numbers, and a range of special operator characters. This makes it possible to use standard mathematical operators (e.g., The Scala Language Specification defines these operator characters as “all other characters in \u0020-007F and Unicode categories Sm [Symbol/Math] … except parentheses ([]) and periods.” Square brackets (referred to in the text as parentheses) are reserved for use in type parameterization, while periods are reserved for access to the fields and methods of objects (instantiated types). Here are the rules for combining letters, numbers, and characters into valid identifiers in Scala:
NoteNames enclosed in backquotes can, unlike the other names, be reserved keywords in Scala such as Let’s try out some of these naming rules in the REPL: scala> val π = 3.14159 π: Double = 3.14159 scala> val $ = "USD currency symbol" $: String = USD currency symbol scala> val o_O = "Hmm" o_O: String = Hmm scala> val 50cent = "$0.50" <console>:1: error: Invalid literal number val 50cent = "$0.50" ^ scala> val a.b = 25 <console>:7: error: not found: value a val a.b = 25 scala> val `a.b` = 4 a.b: Int = 4 The special character “π” is a valid Scala identifier. The value name “50cent” is invalid because names cannot start with numbers. In this case the compiler started parsing the name as a literal number and ran into problems at the letter “c”. The value name “a.b” is invalid because a period isn’t an operator character. Rewriting this value with backquotes fixes the problem, although the aesthetics of using backquotes isn’t that great. Value and variable names, by convention, should start with a lowercase letter and then capitalize additional words. This is popularly known as camel case, and though not required it is recommended for all Scala developers. This helps to distinguish them from types and classes which (also by convention, not by rule) follow camel case but start with an uppercase letter. TypesScala has both numeric (e.g.,
Unlike Java and C there is no concept of a primitive type in Scala. While the Java Virtual Machine supports the primitive integer type
Numeric Data TypesTable 2-1 displays Scala’s numeric data types. Table 2-1. Core numeric types
NoteSee the API documentation for Scala supports the ability to automatically convert numbers from one type to another based on the rank of the type. The numeric types in
Table 2-1 are sorted by their automatic conversion rank, where the Let’s try this out by creating values of different types and automatically converting them to higher-ranked types: scala> val b: Byte = 10 b: Byte = 10 scala> val s: Short = b s: Short = 10 scala> val d: Double = s d: Double = 10.0 The NoteJava developers will recognize the names of these types, which are wrappers around the core JVM types of the same names (except the JVM’s Scala does not allow automatic conversion from higher ranked types to lower ranked types. This makes sense, because you could otherwise lose data if you convert to a type with less storage. Here is an example of trying to automatically convert a higher ranked type to a lower ranked type and the ensuing error: scala> val l: Long = 20 l: Long = 20 scala> val i: Int = l <console>:8: error: type mismatch; found : Long required: Int val i: Int = l You can choose to manually convert between types using the toType methods available on all numeric types. Although this makes it possible to lose data by converting to a lesser ranked type, it is useful when you know that the data is compatible with the lower ranked type. For example, here is a scala> val l: Long = 20 l: Long = 20 scala> val i: Int = l.toInt i: Int = 20 An alternative to using explicit types is to specify the type of your literal data directly, using Scala’s notation for literal types. See Table 2-2 for the full list of notations for specifying the types of literals. Table 2-2. Numeric literals
Literal Characters Are Case-InsensitiveYou can use either lowercase or uppercase letters in Scala’s literal types. The literal number Let’s try out these literals by assigning them to new values without stating the type. The Scala REPL will use type inference to calculate the appropriate types for each value: scala> val anInt = 5 anInt: Int = 5 scala> val yellowRgb = 0xffff00 yellowRgb: Int = 16776960 scala> val id = 100l id: Long = 100 scala> val pi = 3.1416 pi: Double = 3.1416 StringsThe Write scala> val hello = "Hello There" hello: String = Hello There scala> val signature = "With Regards, \nYour friend" signature: String = With Regards, Your friend Like numeric types, the scala> val greeting = "Hello, " + "World" greeting: String = Hello, World scala> val matched = (greeting == "Hello, World") matched: Boolean = true scala> val theme = "Na " * 16 + "Batman!" // what do you expect this to print? A multiline scala> val greeting = """She suggested reformatting the file | by replacing tabs (\t) with newlines (\n); | "Why do that?", he asked. """ greeting: String = She suggested reformatting the file by replacing tabs (\t) with newlines (\n); "Why do that?", he asked. String interpolationBuilding a scala> val approx = 355/113f approx: Float = 3.141593 scala> println("Pi, using 355/113, is about " + approx + "." ) Pi, using 355/113, is about 3.141593. A more direct way to combine your values or variables inside
a Here is the example again using string interpolation: scala> println(s"Pi, using 355/113, is about $approx." ) Pi, using 355/113, is about 3.141593. You will need the optional braces if you have any nonword characters in your reference (such as a calculation), or if your reference can’t be distinguished from the surrounding text: scala> val item = "apple" item: String = apple scala> s"How do you like them ${item}s?" res0: String = How do you like them apples? scala> s"Fish n chips n vinegar, ${"pepper "*3}salt" res1: String = Fish n chips n vinegar, pepper pepper pepper salt An alternate format for string interpolation uses NoteIf you are unfamiliar with scala> val item = "apple" item: String = apple scala> f"I wrote a new $item%.3s today" res2: String = I wrote a new app today scala> f"Enjoying this $item ${355/113.0}%.5f times today" res3: String = Enjoying this apple 3.14159 times today These Now that we have learned how to control data output with strings, let’s find out how to do the opposite with regular expressions. Regular expressionsA regular expression is a string of characters and punctuation that represents a search pattern. Popularized by Perl and command-line utilities like Grep, regular expressions are a standard feature in the libraries of most programming languages including Scala. The format for Scala’s regular expressions is based on the Java class The Table 2-3. Regular expression operations
For more advanced handling of regular expressions, convert a string to a regular expression type by invoking its Syntax: Capturing Values with Regular Expressions val <Regex value>(<identifier>) = <input string> Let’s try this out by capturing the numeric value from the output of the previous example (see String interpolation). We’ll use multiline strings to store our regular expression pattern, because they are literal and allow us to write a backslash without a second, escaping backslash: scala> val input = "Enjoying this apple 3.14159 times today" input: String = Enjoying this apple 3.14159 times today scala> val pattern = """.* apple ([\d.]+) times .*""".r pattern: scala.util.matching.Regex = .* apple ([\d.]+) times .* scala> val pattern(amountText) = input amountText: String = 3.14159 scala> val amount = amountText.toDouble amount: Double = 3.14159 The capture group is the series of digits and a period between the words The full regular expression type is The format is admittedly a bit odd. The name of the new value containing the capture group match, After converting the amount in text form to a Regular expressions serve as a compact and efficient means to process text, with operations such as matching, replacing, and capturing. If you are still new to regular expressions, it is worth investing time to study them because they are widely applicable in modern software development. An Overview of Scala TypesIn this section we will move on from numbers and strings to a broader look at the range of core types. All of Scala’s types, from numbers to strings to collections, exist as part of a type hierarchy. Every class that you define in Scala will also belong to this hierarchy automatically. Figure 2-1 shows the hierarchy of Scala’s core (numeric and nonnumeric) types. Figure 2-1. The Scala type hierarchy The open-headed arrows in the diagram indicate supertypes, a common notation in object-oriented diagrams. The multiple-arrow types at the bottom indicate that they are subtypes of every type in the system, including classes you define on your own. In Table 2-4 you can see a full listing of the specific types mentioned in this diagram, followed by more complete descriptions.
Table 2-4. Core nonnumeric types
The At the bottom of the Scala type hierarchy are the The other bottom type is
scala> val c = 'A' c: Char = A scala> val i: Int = c i: Int = 65 scala> val t: Char = 116 t: Char = t The scala> val isTrue = !true isTrue: Boolean = false scala> val isFalse = !true isFalse: Boolean = false scala> val unequal = (5 != 6) unequal: Boolean = true scala> val isLess = (5 < 6) isLess: Boolean = true scala> val unequalAndLess = unequal & isLess unequalAndLess: Boolean = true scala> val definitelyFalse = false && unequal definitelyFalse: Boolean = false What is the Difference Between & and && ?The Boolean comparison operators Unlike
many dynamic languages, Scala does not support automatic conversion of other types to Booleans. A nonnull string cannot be evaluated as scala> val zero = 0 zero: Int = 0 scala> val isValid = zero > 0 isValid: Boolean = false The The scala> val nada = () nada: Unit = () Now that we have covered the core types, let’s have a look at the operations they all have in common. Type operationsTable 2-5 displays the operations available on all types in Scala. The Table 2-5. Common type operations
Avoid asInstanceOfThe The types we have covered so far in this chapter are all (with the possible exception of TuplesA tuple is an ordered container of two or more values, all of which may have different types. You may be familiar with this term from working with relational databases, where a single row of a table is considered its own tuple. Tuples can be useful when you need to logically group values, representing them as a coherent unit. Unlike lists and arrays, however, there is no way to iterate through elements in a tuple. Its purpose is only as a container for more than one value. You can create a tuple by writing your values separated by a comma and surrounded by a pair of parentheses. Syntax: Create a Tuple ( <value 1>, <value 2>[, <value 3>...] ) For example, here is a tuple containing scala> val info = (5, "Korben", true) info: (Int, String, Boolean) = (5,Korben,true) You can access an individual element from a tuple by its 1-based index (e.g., where the first element is 1, second is 2, etc.): scala> val name = info._2 name: String = Korben An alternate form of creating a 2-sized tuple is with the relation operator ( scala> val red = "red" -> "0xff0000" red: (String, String) = (red,0xff0000) scala> val reversed = red._2 -> red._1 reversed: (String, String) = (0xff0000,red) Tuples provide a generic means to structure data, and are useful when you need to group discrete elements for handling. SummaryThis may be a challenging chapter to see through to the end, because you had to read all about types and data without learning how to do real programming in Scala yet. I’m glad you did. What was the oddest or most-unexpected part of this chapter? The use of keywords to announce value and variable definition? The reversed manner (if you’re coming from Java) of defining a variable’s name before its type? The idea that much of your code can use fixed, nonreassignable values instead of (variable) variables? If these ideas were hard to take, the good news is that, as you gain experience in Scala developemnt, they will become quite normal. Eventually they may even seem to be obvious choices for a well-designed functional programming language. At this point you should know how to define your own values and variables, although we haven’t yet learned where to come up with useful data to store in them. In the next chapter you will study ways to derive and calculate this data using logical structures known as expressions. Exercises
Which of the following is the name of the Java primitive data type?Primitive data types - includes byte , short , int , long , float , double , boolean and char.
What symbols are required for a compiler to ignore a comment?Sometimes we need comments which span several lines in a program. As long as the block of statements starts with the characters /* and ends with the characters */ those lines will be ignored by the Java compiler/interpreter (i.e. will NOT be executed).
What is the purpose of the Eclipse editor area and views Choose all correct answers?What is the purpose of the Eclipse Editor Area and Views? Mark for Review (1) Points (Choose all correct answers) To modify elements. (*) To navigate a hierarchy of information. (*) To choose the file system location to delete a file.
Which of the following declares and initializes a two dimensional array that can hold 6 object reference types?29. Which of the following declares and initializes a two dimensional array that can hold 6 Object reference types? String[] array=new String[6]; Object array=new Object[6]; Object[][] array=new Object[2][3]; (*) String[][] array=String[6]; 30.
|