- •Contents
- •List of Figures
- •List of Tables
- •List of Listings
- •Foreword
- •Foreword to the First Edition
- •Acknowledgments
- •Introduction
- •A Scalable Language
- •A language that grows on you
- •What makes Scala scalable?
- •Why Scala?
- •Conclusion
- •First Steps in Scala
- •Conclusion
- •Next Steps in Scala
- •Conclusion
- •Classes and Objects
- •Semicolon inference
- •Singleton objects
- •A Scala application
- •Conclusion
- •Basic Types and Operations
- •Some basic types
- •Literals
- •Operators are methods
- •Arithmetic operations
- •Relational and logical operations
- •Bitwise operations
- •Object equality
- •Operator precedence and associativity
- •Rich wrappers
- •Conclusion
- •Functional Objects
- •Checking preconditions
- •Self references
- •Auxiliary constructors
- •Method overloading
- •Implicit conversions
- •A word of caution
- •Conclusion
- •Built-in Control Structures
- •If expressions
- •While loops
- •For expressions
- •Match expressions
- •Variable scope
- •Conclusion
- •Functions and Closures
- •Methods
- •Local functions
- •Short forms of function literals
- •Placeholder syntax
- •Partially applied functions
- •Closures
- •Special function call forms
- •Tail recursion
- •Conclusion
- •Control Abstraction
- •Reducing code duplication
- •Simplifying client code
- •Currying
- •Writing new control structures
- •Conclusion
- •Composition and Inheritance
- •A two-dimensional layout library
- •Abstract classes
- •Extending classes
- •Invoking superclass constructors
- •Polymorphism and dynamic binding
- •Using composition and inheritance
- •Heighten and widen
- •Putting it all together
- •Conclusion
- •How primitives are implemented
- •Bottom types
- •Conclusion
- •Traits
- •How traits work
- •Thin versus rich interfaces
- •Example: Rectangular objects
- •The Ordered trait
- •Why not multiple inheritance?
- •To trait, or not to trait?
- •Conclusion
- •Packages and Imports
- •Putting code in packages
- •Concise access to related code
- •Imports
- •Implicit imports
- •Package objects
- •Conclusion
- •Assertions and Unit Testing
- •Assertions
- •Unit testing in Scala
- •Informative failure reports
- •Using JUnit and TestNG
- •Property-based testing
- •Organizing and running tests
- •Conclusion
- •Case Classes and Pattern Matching
- •A simple example
- •Kinds of patterns
- •Pattern guards
- •Pattern overlaps
- •Sealed classes
- •The Option type
- •Patterns everywhere
- •A larger example
- •Conclusion
- •Working with Lists
- •List literals
- •The List type
- •Constructing lists
- •Basic operations on lists
- •List patterns
- •First-order methods on class List
- •Methods of the List object
- •Processing multiple lists together
- •Conclusion
- •Collections
- •Sequences
- •Sets and maps
- •Selecting mutable versus immutable collections
- •Initializing collections
- •Tuples
- •Conclusion
- •Stateful Objects
- •What makes an object stateful?
- •Reassignable variables and properties
- •Case study: Discrete event simulation
- •A language for digital circuits
- •The Simulation API
- •Circuit Simulation
- •Conclusion
- •Type Parameterization
- •Functional queues
- •Information hiding
- •Variance annotations
- •Checking variance annotations
- •Lower bounds
- •Contravariance
- •Object private data
- •Upper bounds
- •Conclusion
- •Abstract Members
- •A quick tour of abstract members
- •Type members
- •Abstract vals
- •Abstract vars
- •Initializing abstract vals
- •Abstract types
- •Path-dependent types
- •Structural subtyping
- •Enumerations
- •Case study: Currencies
- •Conclusion
- •Implicit Conversions and Parameters
- •Implicit conversions
- •Rules for implicits
- •Implicit conversion to an expected type
- •Converting the receiver
- •Implicit parameters
- •View bounds
- •When multiple conversions apply
- •Debugging implicits
- •Conclusion
- •Implementing Lists
- •The List class in principle
- •The ListBuffer class
- •The List class in practice
- •Functional on the outside
- •Conclusion
- •For Expressions Revisited
- •For expressions
- •The n-queens problem
- •Querying with for expressions
- •Translation of for expressions
- •Going the other way
- •Conclusion
- •The Scala Collections API
- •Mutable and immutable collections
- •Collections consistency
- •Trait Traversable
- •Trait Iterable
- •Sets
- •Maps
- •Synchronized sets and maps
- •Concrete immutable collection classes
- •Concrete mutable collection classes
- •Arrays
- •Strings
- •Performance characteristics
- •Equality
- •Views
- •Iterators
- •Creating collections from scratch
- •Conversions between Java and Scala collections
- •Migrating from Scala 2.7
- •Conclusion
- •The Architecture of Scala Collections
- •Builders
- •Factoring out common operations
- •Integrating new collections
- •Conclusion
- •Extractors
- •An example: extracting email addresses
- •Extractors
- •Patterns with zero or one variables
- •Variable argument extractors
- •Extractors and sequence patterns
- •Extractors versus case classes
- •Regular expressions
- •Conclusion
- •Annotations
- •Why have annotations?
- •Syntax of annotations
- •Standard annotations
- •Conclusion
- •Working with XML
- •Semi-structured data
- •XML overview
- •XML literals
- •Serialization
- •Taking XML apart
- •Deserialization
- •Loading and saving
- •Pattern matching on XML
- •Conclusion
- •Modular Programming Using Objects
- •The problem
- •A recipe application
- •Abstraction
- •Splitting modules into traits
- •Runtime linking
- •Tracking module instances
- •Conclusion
- •Object Equality
- •Equality in Scala
- •Writing an equality method
- •Recipes for equals and hashCode
- •Conclusion
- •Combining Scala and Java
- •Using Scala from Java
- •Annotations
- •Existential types
- •Using synchronized
- •Compiling Scala and Java together
- •Conclusion
- •Actors and Concurrency
- •Trouble in paradise
- •Actors and message passing
- •Treating native threads as actors
- •Better performance through thread reuse
- •Good actors style
- •A longer example: Parallel discrete event simulation
- •Conclusion
- •Combinator Parsing
- •Example: Arithmetic expressions
- •Running your parser
- •Basic regular expression parsers
- •Another example: JSON
- •Parser output
- •Implementing combinator parsers
- •String literals and regular expressions
- •Lexing and parsing
- •Error reporting
- •Backtracking versus LL(1)
- •Conclusion
- •GUI Programming
- •Panels and layouts
- •Handling events
- •Example: Celsius/Fahrenheit converter
- •Conclusion
- •The SCells Spreadsheet
- •The visual framework
- •Disconnecting data entry and display
- •Formulas
- •Parsing formulas
- •Evaluation
- •Operation libraries
- •Change propagation
- •Conclusion
- •Scala Scripts on Unix and Windows
- •Glossary
- •Bibliography
- •About the Authors
- •Index
Section 31.3 |
Chapter 31 · Combining Scala and Java |
718 |
Java 1.5, many reflection objects have a getAnnotation method for searching for annotations of a specific type. In this case, the code looks for an annotation of our new Ignore type. Since this is a Java API, success is indicated by whether the result is null or is an actual annotation object.
Here is the code in action:
$ javac Ignore.java $ scalac Tests.scala
$ scalac FindTests.scala $ scala FindTests
found a test method: public void Tests$.test2() found a test method: public void Tests$.test1()
As an aside, notice that the methods are in class Tests$ instead of class Tests when viewed with Java reflection. As described at the beginning of the chapter, the implementation of a Scala singleton object is placed in a Java class with a dollar sign added to the end of its name. In this case, the implementation of Tests is in the Java class Tests$.
Be aware that when you use Java annotations you have to work within their limitations. For example, you can only use constants, not expressions, in the arguments to annotations. You can support @serial(1234) but not @serial(x * 2), because x * 2 is not a constant.
31.3 Existential types
All Java types have a Scala equivalent. This is necessary so that Scala code can access any legal Java class. Most of the time the translation is straightforward. Pattern in Java is Pattern in Scala, and Iterator<Component> in Java is Iterator[Component] in Scala. For some cases, though, the Scala types you have seen so far are not enough. What can be done with Java wildcard types such as Iterator<?> or Iterator<? extends Component>? What can be done about raw types like Iterator, where the type parameter is omitted? For wildcard types and raw types, Scala uses an extra kind of type called an existential type.
Existential types are a fully supported part of the language, but in practice they are mainly used when accessing Java types from Scala. This section gives a brief overview of how existential types work, but mostly this is only
Cover · Overview · Contents · Discuss · Suggest · Glossary · Index
Section 31.3 |
Chapter 31 · Combining Scala and Java |
719 |
useful so that you can understand compiler error messages when your Scala code accesses Java code.
The general form of an existential type is as follows:
type forSome { declarations }
The type part is an arbitrary Scala type, and the declarations part is a list of abstract vals and types. The interpretation is that the declared variables and types exist but are unknown, just like abstract members of a class. The type is then allowed to refer to the declared variables and types even though it is unknown what they refer to.
Take a look at some concrete examples. A Java Iterator<?> would be written in Scala as:
Iterator[T] forSome { type T }
Read this from left to right. This is an Iterator of T’s for some type T. The type T is unknown, and could be anything, but it is known to be fixed for this particular Iterator. Similarly, a Java Iterator<? extends Component> would be viewed in Scala as:
Iterator[T] forSome { type T <: Component }
This is an Iterator of T, for some type T that is a subtype of Component. In this case T is still unknown, but now it is sure to be a subtype of Component.
By the way, there is a shorter way to write these examples. If you write
Iterator[_], it means the same thing as Iterator[T] forSome { type T }. This is placeholder syntax for existential types, and is similar in spirit to the placeholder syntax for function literals that was described in Section 8.5. If you use an underscore (_) in place of an expression, then Scala treats this as a placeholder and makes a function literal for you. For types it works similarly. If you use an underscore in place of a type, Scala makes an existential type for you. Each underscore becomes one type parameter in a forSome clause, so if you use two underscores in the same type, you will get the effect of a forSome clause with two types in it.
You can also insert upper and lower bounds when using this placeholder syntax. Simply add them to the underscore instead of in the forSome clause. The type Iterator[_ <: Component] is the same as this one, which you just saw:
Iterator[T] forSome { type T <: Component }
Cover · Overview · Contents · Discuss · Suggest · Glossary · Index
Section 31.3 |
Chapter 31 · Combining Scala and Java |
720 |
Enough about the existential types themselves. How do you actually use them? Well, in simple cases, you use an existential type just as if the forSome were not there. Scala will check that the program is sound even though the types and values in the forSome clause are unknown. For example, suppose you had the following Java class:
// This is a Java class with wildcards public class Wild {
Collection<?> contents() {
Collection<String> stuff = new Vector<String>(); stuff.add("a");
stuff.add("b");
stuff.add("see"); return stuff;
}
}
If you access this in Scala code you will see that it has an existential type:
scala> val contents = (new Wild).contents
contents: java.util.Collection[?0] forSome { type ?0 } = [a, b, see]
If you want to find out how many elements are in this collection, you can simply ignore the existential part and call the size method as normal:
scala> contents.size() res0: Int = 3
In more complicated cases, existential types can be more awkward, because there is no way to name the existential type. For example, suppose you wanted to create a mutable Scala set and initialize it with the elements of contents:
import scala.collection.mutable.Set val iter = (new Wild).contents.iterator
val set = Set.empty[???] |
// what type goes here? |
|
while |
(iter.hasMore) |
|
set |
+= iter.next() |
|
Cover · Overview · Contents · Discuss · Suggest · Glossary · Index
Section 31.3 |
Chapter 31 · Combining Scala and Java |
721 |
A problem strikes on the third line. There is no way to name the type of elements in the Java collection, so you cannot write down a satisfactory type for set. To work around this kind of problem, here are two tricks you should consider:
1.When passing an existential type into a method, move type parameters from the forSome clause to type parameters of the method. Inside the body of the method, you can use the type parameters to refer to the types that were in the forSome clause.
2.Instead of returning an existential type from a method, return an object that has abstract members for each of the types in the forSome clause. (See Chapter 20 for information on abstract members.)
Using these two tricks together, the previous code can be written as follows:
import scala.collection.mutable.Set import java.util.Collection
abstract class SetAndType { type Elem
val set: Set[Elem]
}
def javaSet2ScalaSet[T](jset: Collection[T]): SetAndType = { val sset = Set.empty[T] // now T can be named!
val iter = jset.iterator while (iter.hasNext)
sset += iter.next()
return new SetAndType { type Elem = T
val set = sset
}
}
You can see why Scala code normally does not use existential types. To do anything sophisticated with them, you tend to convert them to use abstract members. So you may as well use abstract members to begin with.
Cover · Overview · Contents · Discuss · Suggest · Glossary · Index