Author:michel Schinz,philipp Haller
1. Introduction
This article will introduce the Scala language and its compilation. This assumes that the reader already has some Java development experience and needs to get an overview of what they can do with Scala.
2. A first example
We started with the most famous code in the world. It's not useful, but it's a great way to get a good visual understanding of Scala:
Object HelloWorld { def main (args:array[string]): Unit = { println ("Hello, world!") } }
It's nothing complicated. Only one point, this contains the object declaration of the main method. The declaration of this object is defined by a single case. So, the above declaration defines a class and an instance of this class, which is also called HelloWorld. This instance is created the first time it is used.
The attentive reader will find that this main method is not and is declared static. This is because static members do not exist in Scala. When a static member is required, these members are placed in a singleton (object declaration).
2.1 Compiling This example
To compile using the Scalac command.
>scalac Helloworld.scala
Build the Helloworld.class file after compiling.
2.2 Run this example
After compiling Scala code can be run using Scala commands. The use of these commands is very similar to Java.
>scala-classpath. HelloWorld3 and Java Interop
One of the advantages of Scala is that Java code can be easily used. All classes of the Java.lang package are imported by default, and others can be displayed as well. The following code will show this.
import Java.util. {Date, Locale} Import java.text.DateFormat import java.text.dateformat._ /** * Created by home on 3/11/15. * /Object Frenchdate { def main (args:array[string]): Unit = { val now = new Date val df = Getdat Einstance (LONG, locale.france) println (DF format now) } }
Scala's import statements are almost the same as in Java, but more flexible. A package of multiple classes can be quoted in curly braces in an import statement. Another difference is that you use underscores instead of asterisks to introduce all the classes in the package. Therefore, the third line import statement introduces all the members of the DateFormat.
In the main function, you first create a Java date class instance, which contains the current time by default. A date format is then defined with the static method gerdateinstance . Finally the time of the printing format after France. The last df format now one is a very interesting Scala syntax. a method that requires only one parameter can be used in infix syntax . According to the general wording is this: df.format(now) .
Finally, it is important to note that Scala can inherit a class directly from Java, or implement Java interfaces.
4 Everything is an object
Scala is a pure object-oriented language, so all of its internals are objects. Include numbers and methods. In this bunch of faces, Scala and Java are a lot different. Because Java distinguishes between value types and reference types, it does not allow methods to be treated as values.
4.1 Numbers and objects
Because Scala's numbers are objects, you can include methods. Such as:
1 + 2 * 3/x
After the actual method call is restored:
(1). (((2). * (3))./(x) )
This also shows that the +, * and other symbols are available in Scala.
4.2 Methods are also objects
This may be even more surprising to Java developers. The method is also an object of Scala. So you can pass the method as a parameter, assign them to the variable, and return from the other method. This also gives Scala another ability: functional programming.
Here's an example to illustrate this. There is a method that needs to be executed once per second oncePerSecond , and there is a callback method as a parameter. () => Unitthe notation is to define a method that returns a unit without parameters (the unit is equivalent to the void in C/s + +). Code:
Object Timer { def oncepersecond (callback: () = = unit): unit = { while (true) { callback (); Thread sleep + } def timeflies (): Unit = { println ("Time flies like an arrow ...") } c12> def main (args:array[string]): Unit = { Oncepersecond (timeflies) } } /c10>
In order to output the string, we use the predefined println method instead of the System.out.
4.2.1 Anonymous method
Because the code above is very easy to understand, it can be streamlined. First, the method timeflies is defined just to be passed as a parameter to the method oncePerSecond . Once this method is named, it is used only once. Very not necessary. Using Scala's anonymous approach can save you the hassle. Such as:
Object timeranonymous { def oncepersecond (callback: () = unit): unit = { while (true) { callback (); Thread sleep + } def main (args:array[string]): Unit = { Oncepersecond () = Print ln ("Time flies like an arrow ...") } }
The above anonymous function uses = = to differentiate the function's argument list and function body. In this example, the argument list for the function is empty, so the parentheses are empty. The function body is the same as the timeflies in the above example.
Class 5
As mentioned above, Scala is an object-oriented language, so there is also the concept of class . Scala's class declarations are basically the same as in Java, except that Scala's class definitions can contain parameters, as follows:
class Comlex (real:double, imaginary:double) { def re () = Real def im () = Imaginary }
This class of complex requires two parameters. These parameters must be provided when the instance is created, such as: new Complex(1.5, 2.3) . This class contains two method members, re and IM, and can get two corresponding parameter values.
The return type of the two member method is not shown. The compiler automatically infers it. The compiler does not always automatically infer the return type, and there is no rule to know when the compiler cannot automatically infer it. This does not cause any distress, because the compiler will give warning when it is not inferred. It is best for a novice developer to give a type to see if the compiler agrees (warning).
5.1 Parameter-free method
The above example also has a small problem. You have to write two empty parentheses when calling the Re,im method above.
def main (args:array[string]): Unit = { val c = new Complex (1.3, 4.6) println ("Imaginary part:" + c.im ()) }
It's not convenient. This is possible in Scala.
class Complex (real:double, imaginary:double) { def re = real def im = imaginary } object Timeranon ymous { def main (args:array[string]): Unit = { val c = new Complex (1.3, 4.6) println ("Imaginary part:" + c.im) } }
The brackets do not add to all.
5.2 Inheritance and override
Scala's classes are all inherited from a superclass. If it is not shown to the superclass, such as the complex class above, then the default is inherited from Scala. Anyref. You can also override the superclass method in Scala. The override keyword is clearly marked when you override. Such as:
class Complex (real:double, imaginary:double) { def re = real def im = imaginary override def toString () = "" + Re + (if (im < 0) "" Else "+") +im + "I" } 6 case class and pattern matching
A data structure that is often present in a program is a tree. For example, inside the parser and compiler, the program is rendered as a tree, the XML document is a tree, and so on. We'll go through a small calculator program to see how many trees in Scala need to be handled. This program is used to deal with very simple algebraic expressions that contain constants and variables. Two examples are 1 + 2 and (x + x) + (7 + y).
First we need to be clear about how this type of expression should be presented. Obviously a tree: the operator is a node, and the leaf of that node is a constant or variable.
In Java, such a tree is handled using an abstract superclass. The subclass of this abstract superclass then handles a node or a leaf. In functional programming languages, you can use algebraic types to achieve the same purpose. Scala's case class is a concept between the two. Such as:
abstract class Tree Case class Sum (L:tree, r:tree) extends tree case class Var (n:string) extends tree c ASE class Const (v:int) extends Tree
Class Sum, Var, and Const are declared as case classes, indicating that they are different in some ways from the standard class:
- The new keyword is not required when creating an instance (such as: const (5), without writing the new const (5)),
- Getter methods are automatically based on the parameters of the constructor (such as instance C of the Const class, the value of the constructor parameter V from C can be obtained by C.V),
- Provides the default
toString method and prints according to the most basic calling method. For example: X+1 print as: Sum (Var (x), Const (1)),
- Instances of these classes can be decomposed using pattern matching, as described below.
We have defined the data types that express algebraic expressions. You can then define the actions above them. Let's define a method to derive the value of this expression under certain conditions . These "conditions" are some specific values for a given variable. For example, the expression x+1 a value of 6 when a certain condition (the value of X is 5).
So we need to find a way to represent this condition. You would think of hash table as a data structure, but in Scala we can use the method directly a specific condition is nothing more than a variable and the value that the variable has at this time. The condition of {x = 5} mentioned above can be expressed simply as:
{case "x" = 5}
The above expression defines a function. When the argument is a string of x, the value 5 is returned, and the exception is thrown in other cases.
Before we go any further, we give this condition type a name. We can of course use String = Int. But it would be a lot easier if we gave a name. You can do this in Scala:
type environment = String = = Int
From now on, type environment can represent a function of string to int.
We can now give the derivation function. The concept is very simple: the two expression is the same as the value of the two expressions. The value of a constant is the constant itself. The concept translates into Scala very easily:
def eval (T:tree, env:environment): Int = t match {case Sum (l, r) = eval (l, Env) + eval (r, Env) case Var (N) = env (n) Case Const (v) = v }
This derivation method uses pattern matching to process the tree. The meaning of the above expression is already obvious:
- Checks if the expression is sum, and if so it contains the Saozi right subtree, l and R, respectively. Then continue to derive the expression after the arrow;
- If the first expression is not executed successfully, perhaps the tree is not a sum. Continue checking if T (formal parameter) is Var, and if it is, bind the var node containing the value and the n variable and continue processing the right-hand expression.
- If a second check fails, then T is neither Sum nor var. Checks if T is a const and, if it is, binds the const node containing the value and the V variable and continues to handle the right-handed expression.
- Finally, if all checks fail, an exception is thrown. Here, it happens only when more of the tree subclasses are defined.
You will find that pattern matching is the matching of a value to a series of patterns. Once a match is made, the different parts of the value are extracted and named. Finally, the final derivation is made using these named components.
An experienced object-oriented developer will ask why we don't define eval as a method of class tree and his subclasses. We can do this because Scala allows you to define methods in the case class. When to use pattern matching, when to use the method is a personal taste of things. However, there are some very obvious experiences that can be drawn from:
- When using a method, it is easier to add a new node: You only need to define a new tree subclass. On the other hand, adding a new operation to the tree (such as subtraction, multiplication, etc.) is more troublesome. Because, you need to make a corresponding modification to each subclass.
- When using pattern matching, the opposite is the case. Adding a new node requires modifying all of the pattern-matching methods. Adding an operation is straightforward, and you need to define a different method.
7 Interface (Traits)
In addition to inheriting from a superclass, a Scala class can implement one or more traits.
One of the easiest ways for a Java developer to understand trait is to tell him that this is Java's interface. Java's interface can also contain code. In Scala, when a class inherits the trait, he implements the trait interface and inherits all of the trait's code.
We use a classic case to demonstrate the features of the trait. Sorting the list of objects is a frequently used feature. Java comparison will implement the comparable interface. Scala can do a little better than simply implementing a comparable trait. Here we define a trait for Ord. The statement reads as follows:
trait Ord { def < (That:any): Boolean def <= (that:any): Boolean = (This < that) | | (This = =) def > (that:any): Boolean =! (This <=) def >= (that:any): Boolean =! (This < that) }
The above declaration creates a new type, Ord, like the Java comparable interface function, and gives three comparison default implementations and an abstract method. The Equals and not equals comparison does not appear, because those are the default implementations of any object. The Any type is a superclass of all other types in Scala. These include the basic type int, float, and so on.
As an example, we will define a date class to implement the above interface to achieve the purpose of comparison. This date class uses GMT, which contains day, month, and year, all of which are of type int.
class Date (Y:int, M:int, d:int) extends Ord { def year = y def month = M def day = d Override Def toString (): String = year + "-" + month + "-" + Day }
First extends the Ord, and defines the corresponding date and date parameters. We then redefine the Equals method so that we compare two times in different parts of the time.
override Def equals (That:any): Boolean = That.isinstanceof[date] && { val o = that.asinstanceof[date] c3> O.day = = Day && O.month = = Month && o.year = year }
This method uses the built-in isInstanceOf and asInstanceOf . The first isInstanceOf method that is equivalent to Java instanceof . Returns True when the object is an instance of a type. The second asInstanceOf is a type conversion operator that is equivalent to Java. If an instance is of a certain type, it can be converted. Otherwise, the classcastexception exception is thrown.
The last method to implement is less than the comparison. This will use a different, predefined method error that throws an exception for the given message.
def < (that:any): Boolean = { if (!that.isinstanceof[date]) sys.error ("Cannot compare" + that + "and a Date ") val o = that.asinstanceof[date] (Year < o.year) | | (Year = = O.year && (Month < O.month | | (Month = = O.month && Day < O.day))) }
This completes the definition of the date class. Instances of this class have also been considered as dates and can also be treated as comparable objects. Traits is more flexible than shown in the above example. The reader can delve into the study.
8 generic type
The Scala feature to be discussed at the end of this article is generics. Generics can write code that is of type parameters. The most typical example is the STL of C + +. In the Java1.5 before the STL, the list of the list can only accept object as the argument, after the specific type of fetching objects and then forced type conversion. Scala can define generic classes and square methods to save you from a bunch of type conversions. The following defines a simplest container class that can be empty or point to an object of a certain type.
class Reference[t] { private var contents:t = _ def set (value:t) { contents = value } def G Et:t = Contents }
The type parameter of this class is called T. This type modifies the contents member inside the class, as well as the value of the set method and the return type of the Get method. It is interesting to say that private var contents: T = _ . The underscore indicates that a default value is assigned to the contents. The default value for the numeric type is 0, the default value for the logical type is False,unit (), and the default value for the other object is null.
See below how to use.
class Integerreference { def main (args:array[string]): Unit = { val cell = new Reference[int] cell. Set (println) ("Reference contains the half of" + (Cell.get * 2) } }
It is calculated directly after get, eliminating the need to do any type conversions.
9 Conclusion
This article just gives you a quick index that allows you to get to know the language of Scala faster. Interested readers can visit the Scala website for more information. Click here for the original text.
A scala tutorial for Java Developers