From a428be209698c60e1730a4ffb2e7db49d4914f05 Mon Sep 17 00:00:00 2001 From: Christian Krause Date: Tue, 1 May 2012 19:04:12 +0200 Subject: [PATCH 1/6] started scala for java in german --- de/tutorials/scala-for-java-programmers.md | 721 +++++++++++++++++++++ 1 file changed, 721 insertions(+) create mode 100644 de/tutorials/scala-for-java-programmers.md diff --git a/de/tutorials/scala-for-java-programmers.md b/de/tutorials/scala-for-java-programmers.md new file mode 100644 index 0000000000..49b7506c4c --- /dev/null +++ b/de/tutorials/scala-for-java-programmers.md @@ -0,0 +1,721 @@ +--- +layout: overview +title: Eine Scala Anleitung für Java Programmierer +overview: scala-for-java-programmers + +disqus: true +multilingual-overview: true +languages: [en, es, ko, de] +--- + +Von Michel Schinz und Philipp Haller. +Deutsche Übersetzung von Christian Krause. + +## Einleitung + +Diese Anleitung soll als kurze Vorstellung der Programmiersprache Scala und deren Compiler dienen. +Sie ist für fortgeschrittene Programmierer gedacht, die sich einen Überblick über Scala verschaffen +wollen und wie man mit ihr arbeitet. Grundkenntnisse in Objekt-orientierter Programmierung, +insbesondere Java, werden vorausgesetzt. + +## Das erste Beispiel + +Als erstes werden wir das wohlbekannte *Hallo, Welt!*-Programm implementieren. Obwohl es sehr +einfach ist, eignet es sich sehr gut, Scala's Funktionsweise zu demonstrieren, ohne dass man viel +über die Sprache wissen muss. So sieht es aus: + + object HalloWelt { + def main(args: Array[String]) { + println("Hallo, Welt!") + } + } + +Die Struktur des Programmes sollte Java Anwendern bekannt vorkommen: es besteht aus einer Methode +namens `main`, welche die Kommandozeilenparameter als Feld (Array) von Zeichenketten (String) +übergeben bekommt. Der Körper dieser Methode besteht aus einem einzelnen Aufruf der vordefinierten +Methode `println`, die die freundliche Begrüßung als Parameter übergeben bekommt. Weiterhin hat die +`main`-Methode keinen Rückgabewert, sie ist also eine Prozedur. Daher ist es auch nicht notwendig, +einen Rückgabetyp zu deklarieren. + +What is less familiar to Java programmers is the `object` +declaration containing the `main` method. Such a declaration +introduces what is commonly known as a *singleton object*, that +is a class with a single instance. The declaration above thus declares +both a class called `HelloWorld` and an instance of that class, +also called `HelloWorld`. This instance is created on demand, +the first time it is used. + +The astute reader might have noticed that the `main` method is +not declared as `static` here. This is because static members +(methods or fields) do not exist in Scala. Rather than defining static +members, the Scala programmer declares these members in singleton +objects. + +### Compiling the example + +To compile the example, we use `scalac`, the Scala compiler. `scalac` +works like most compilers: it takes a source file as argument, maybe +some options, and produces one or several object files. The object +files it produces are standard Java class files. + +If we save the above program in a file called +`HelloWorld.scala`, we can compile it by issuing the following +command (the greater-than sign `>` represents the shell prompt +and should not be typed): + + > scalac HelloWorld.scala + +This will generate a few class files in the current directory. One of +them will be called `HelloWorld.class`, and contains a class +which can be directly executed using the `scala` command, as the +following section shows. + +### Running the example + +Once compiled, a Scala program can be run using the `scala` command. +Its usage is very similar to the `java` command used to run Java +programs, and accepts the same options. The above example can be +executed using the following command, which produces the expected +output: + + > scala -classpath . HelloWorld + + Hello, world! + +## Interaction with Java + +One of Scala's strengths is that it makes it very easy to interact +with Java code. All classes from the `java.lang` package are +imported by default, while others need to be imported explicitly. + +Let's look at an example that demonstrates this. We want to obtain +and format the current date according to the conventions used in a +specific country, say France. (Other regions such as the +French-speaking part of Switzerland use the same conventions.) + +Java's class libraries define powerful utility classes, such as +`Date` and `DateFormat`. Since Scala interoperates +seemlessly with Java, there is no need to implement equivalent +classes in the Scala class library--we can simply import the classes +of the corresponding Java packages: + + import java.util.{Date, Locale} + import java.text.DateFormat + import java.text.DateFormat._ + + object FrenchDate { + def main(args: Array[String]) { + val now = new Date + val df = getDateInstance(LONG, Locale.FRANCE) + println(df format now) + } + } + +Scala's import statement looks very similar to Java's equivalent, +however, it is more powerful. Multiple classes can be imported from +the same package by enclosing them in curly braces as on the first +line. Another difference is that when importing all the names of a +package or class, one uses the underscore character (`_`) instead +of the asterisk (`*`). That's because the asterisk is a valid +Scala identifier (e.g. method name), as we will see later. + +The import statement on the third line therefore imports all members +of the `DateFormat` class. This makes the static method +`getDateInstance` and the static field `LONG` directly +visible. + +Inside the `main` method we first create an instance of Java's +`Date` class which by default contains the current date. Next, we +define a date format using the static `getDateInstance` method +that we imported previously. Finally, we print the current date +formatted according to the localized `DateFormat` instance. This +last line shows an interesting property of Scala's syntax. Methods +taking one argument can be used with an infix syntax. That is, the +expression + + df format now + +is just another, slightly less verbose way of writing the expression + + df.format(now) + +This might seem like a minor syntactic detail, but it has important +consequences, one of which will be explored in the next section. + +To conclude this section about integration with Java, it should be +noted that it is also possible to inherit from Java classes and +implement Java interfaces directly in Scala. + +## Everything is an Object + +Scala is a pure object-oriented language in the sense that +*everything* is an object, including numbers or functions. It +differs from Java in that respect, since Java distinguishes +primitive types (such as `boolean` and `int`) from reference +types, and does not enable one to manipulate functions as values. + +### Numbers are objects + +Since numbers are objects, they also have methods. And in fact, an +arithmetic expression like the following: + + 1 + 2 * 3 / x + +consists exclusively of method calls, because it is equivalent to the +following expression, as we saw in the previous section: + + (1).+(((2).*(3))./(x)) + +This also means that `+`, `*`, etc. are valid identifiers +in Scala. + +The parentheses around the numbers in the second version are necessary +because Scala's lexer uses a longest match rule for tokens. +Therefore, it would break the following expression: + + 1.+(2) + +into the tokens `1.`, `+`, and `2`. The reason that +this tokenization is chosen is because `1.` is a longer valid +match than `1`. The token `1.` is interpreted as the +literal `1.0`, making it a `Double` rather than an +`Int`. Writing the expression as: + + (1).+(2) + +prevents `1` from being interpreted as a `Double`. + +### Functions are objects + +Perhaps more surprising for the Java programmer, functions are also +objects in Scala. It is therefore possible to pass functions as +arguments, to store them in variables, and to return them from other +functions. This ability to manipulate functions as values is one of +the cornerstone of a very interesting programming paradigm called +*functional programming*. + +As a very simple example of why it can be useful to use functions as +values, let's consider a timer function whose aim is to perform some +action every second. How do we pass it the action to perform? Quite +logically, as a function. This very simple kind of function passing +should be familiar to many programmers: it is often used in +user-interface code, to register call-back functions which get called +when some event occurs. + +In the following program, the timer function is called +`oncePerSecond`, and it gets a call-back function as argument. +The type of this function is written `() => Unit` and is the type +of all functions which take no arguments and return nothing (the type +`Unit` is similar to `void` in C/C++). The main function of +this program simply calls this timer function with a call-back which +prints a sentence on the terminal. In other words, this program +endlessly prints the sentence "time flies like an arrow" every +second. + + object Timer { + def oncePerSecond(callback: () => Unit) { + while (true) { callback(); Thread sleep 1000 } + } + def timeFlies() { + println("time flies like an arrow...") + } + def main(args: Array[String]) { + oncePerSecond(timeFlies) + } + } + +Note that in order to print the string, we used the predefined method +`println` instead of using the one from `System.out`. + +#### Anonymous functions + +While this program is easy to understand, it can be refined a bit. +First of all, notice that the function `timeFlies` is only +defined in order to be passed later to the `oncePerSecond` +function. Having to name that function, which is only used once, might +seem unnecessary, and it would in fact be nice to be able to construct +this function just as it is passed to `oncePerSecond`. This is +possible in Scala using *anonymous functions*, which are exactly +that: functions without a name. The revised version of our timer +program using an anonymous function instead of *timeFlies* looks +like that: + + object TimerAnonymous { + def oncePerSecond(callback: () => Unit) { + while (true) { callback(); Thread sleep 1000 } + } + def main(args: Array[String]) { + oncePerSecond(() => + println("time flies like an arrow...")) + } + } + +The presence of an anonymous function in this example is revealed by +the right arrow `=>` which separates the function's argument +list from its body. In this example, the argument list is empty, as +witnessed by the empty pair of parenthesis on the left of the arrow. +The body of the function is the same as the one of `timeFlies` +above. + +## Classes + +As we have seen above, Scala is an object-oriented language, and as +such it has a concept of class. (For the sake of completeness, + it should be noted that some object-oriented languages do not have + the concept of class, but Scala is not one of them.) +Classes in Scala are declared using a syntax which is close to +Java's syntax. One important difference is that classes in Scala can +have parameters. This is illustrated in the following definition of +complex numbers. + + class Complex(real: Double, imaginary: Double) { + def re() = real + def im() = imaginary + } + +This complex class takes two arguments, which are the real and +imaginary part of the complex. These arguments must be passed when +creating an instance of class `Complex`, as follows: `new + Complex(1.5, 2.3)`. The class contains two methods, called `re` +and `im`, which give access to these two parts. + +It should be noted that the return type of these two methods is not +given explicitly. It will be inferred automatically by the compiler, +which looks at the right-hand side of these methods and deduces that +both return a value of type `Double`. + +The compiler is not always able to infer types like it does here, and +there is unfortunately no simple rule to know exactly when it will be, +and when not. In practice, this is usually not a problem since the +compiler complains when it is not able to infer a type which was not +given explicitly. As a simple rule, beginner Scala programmers should +try to omit type declarations which seem to be easy to deduce from the +context, and see if the compiler agrees. After some time, the +programmer should get a good feeling about when to omit types, and +when to specify them explicitly. + +### Methods without arguments + +A small problem of the methods `re` and `im` is that, in +order to call them, one has to put an empty pair of parenthesis after +their name, as the following example shows: + + object ComplexNumbers { + def main(args: Array[String]) { + val c = new Complex(1.2, 3.4) + println("imaginary part: " + c.im()) + } + } + +It would be nicer to be able to access the real and imaginary parts +like if they were fields, without putting the empty pair of +parenthesis. This is perfectly doable in Scala, simply by defining +them as methods *without arguments*. Such methods differ from +methods with zero arguments in that they don't have parenthesis after +their name, neither in their definition nor in their use. Our +`Complex` class can be rewritten as follows: + + class Complex(real: Double, imaginary: Double) { + def re = real + def im = imaginary + } + + +### Inheritance and overriding + +All classes in Scala inherit from a super-class. When no super-class +is specified, as in the `Complex` example of previous section, +`scala.AnyRef` is implicitly used. + +It is possible to override methods inherited from a super-class in +Scala. It is however mandatory to explicitly specify that a method +overrides another one using the `override` modifier, in order to +avoid accidental overriding. As an example, our `Complex` class +can be augmented with a redefinition of the `toString` method +inherited from `Object`. + + class Complex(real: Double, imaginary: Double) { + def re = real + def im = imaginary + override def toString() = + "" + re + (if (im < 0) "" else "+") + im + "i" + } + + +## Case Classes and Pattern Matching + +A kind of data structure that often appears in programs is the tree. +For example, interpreters and compilers usually represent programs +internally as trees; XML documents are trees; and several kinds of +containers are based on trees, like red-black trees. + +We will now examine how such trees are represented and manipulated in +Scala through a small calculator program. The aim of this program is +to manipulate very simple arithmetic expressions composed of sums, +integer constants and variables. Two examples of such expressions are +`1+2` and `(x+x)+(7+y)`. + +We first have to decide on a representation for such expressions. The +most natural one is the tree, where nodes are operations (here, the +addition) and leaves are values (here constants or variables). + +In Java, such a tree would be represented using an abstract +super-class for the trees, and one concrete sub-class per node or +leaf. In a functional programming language, one would use an algebraic +data-type for the same purpose. Scala provides the concept of +*case classes* which is somewhat in between the two. Here is how +they can be used to define the type of the trees for our example: + + abstract class Tree + case class Sum(l: Tree, r: Tree) extends Tree + case class Var(n: String) extends Tree + case class Const(v: Int) extends Tree + +The fact that classes `Sum`, `Var` and `Const` are +declared as case classes means that they differ from standard classes +in several respects: + +- the `new` keyword is not mandatory to create instances of + these classes (i.e., one can write `Const(5)` instead of + `new Const(5)`), +- getter functions are automatically defined for the constructor + parameters (i.e., it is possible to get the value of the `v` + constructor parameter of some instance `c` of class + `Const` just by writing `c.v`), +- default definitions for methods `equals` and + `hashCode` are provided, which work on the *structure* of + the instances and not on their identity, +- a default definition for method `toString` is provided, and + prints the value in a "source form" (e.g., the tree for expression + `x+1` prints as `Sum(Var(x),Const(1))`), +- instances of these classes can be decomposed through + *pattern matching* as we will see below. + +Now that we have defined the data-type to represent our arithmetic +expressions, we can start defining operations to manipulate them. We +will start with a function to evaluate an expression in some +*environment*. The aim of the environment is to give values to +variables. For example, the expression `x+1` evaluated in an +environment which associates the value `5` to variable `x`, written +`{ x -> 5 }`, gives `6` as result. + +We therefore have to find a way to represent environments. We could of +course use some associative data-structure like a hash table, but we +can also directly use functions! An environment is really nothing more +than a function which associates a value to a (variable) name. The +environment `{ x -> 5 }` given above can simply be written as +follows in Scala: + + { case "x" => 5 } + +This notation defines a function which, when given the string +`"x"` as argument, returns the integer `5`, and fails with an +exception otherwise. + +Before writing the evaluation function, let us give a name to the type +of the environments. We could of course always use the type +`String => Int` for environments, but it simplifies the program +if we introduce a name for this type, and makes future changes easier. +This is accomplished in Scala with the following notation: + + type Environment = String => Int + +From then on, the type `Environment` can be used as an alias of +the type of functions from `String` to `Int`. + +We can now give the definition of the evaluation function. +Conceptually, it is very simple: the value of a sum of two expressions +is simply the sum of the value of these expressions; the value of a +variable is obtained directly from the environment; and the value of a +constant is the constant itself. Expressing this in Scala is not more +difficult: + + 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 evaluation function works by performing *pattern matching* +on the tree `t`. Intuitively, the meaning of the above definition +should be clear: + +1. it first checks if the tree `t` is a `Sum`, and if it + is, it binds the left sub-tree to a new variable called `l` and + the right sub-tree to a variable called `r`, and then proceeds + with the evaluation of the expression following the arrow; this + expression can (and does) make use of the variables bound by the + pattern appearing on the left of the arrow, i.e., `l` and + `r`, +2. if the first check does not succeed, that is, if the tree is not + a `Sum`, it goes on and checks if `t` is a `Var`; if + it is, it binds the name contained in the `Var` node to a + variable `n` and proceeds with the right-hand expression, +3. if the second check also fails, that is if `t` is neither a + `Sum` nor a `Var`, it checks if it is a `Const`, and + if it is, it binds the value contained in the `Const` node to a + variable `v` and proceeds with the right-hand side, +4. finally, if all checks fail, an exception is raised to signal + the failure of the pattern matching expression; this could happen + here only if more sub-classes of `Tree` were declared. + +We see that the basic idea of pattern matching is to attempt to match +a value to a series of patterns, and as soon as a pattern matches, +extract and name various parts of the value, to finally evaluate some +code which typically makes use of these named parts. + +A seasoned object-oriented programmer might wonder why we did not +define `eval` as a *method* of class `Tree` and its +subclasses. We could have done it actually, since Scala allows method +definitions in case classes just like in normal classes. Deciding +whether to use pattern matching or methods is therefore a matter of +taste, but it also has important implications on extensibility: + +- when using methods, it is easy to add a new kind of node as this + can be done just by defining a sub-class of `Tree` for it; on + the other hand, adding a new operation to manipulate the tree is + tedious, as it requires modifications to all sub-classes of + `Tree`, +- when using pattern matching, the situation is reversed: adding a + new kind of node requires the modification of all functions which do + pattern matching on the tree, to take the new node into account; on + the other hand, adding a new operation is easy, by just defining it + as an independent function. + +To explore pattern matching further, let us define another operation +on arithmetic expressions: symbolic derivation. The reader might +remember the following rules regarding this operation: + +1. the derivative of a sum is the sum of the derivatives, +2. the derivative of some variable `v` is one if `v` is the + variable relative to which the derivation takes place, and zero + otherwise, +3. the derivative of a constant is zero. + +These rules can be translated almost literally into Scala code, to +obtain the following definition: + + def derive(t: Tree, v: String): Tree = t match { + case Sum(l, r) => Sum(derive(l, v), derive(r, v)) + case Var(n) if (v == n) => Const(1) + case _ => Const(0) + } + +This function introduces two new concepts related to pattern matching. +First of all, the `case` expression for variables has a +*guard*, an expression following the `if` keyword. This +guard prevents pattern matching from succeeding unless its expression +is true. Here it is used to make sure that we return the constant `1` +only if the name of the variable being derived is the same as the +derivation variable `v`. The second new feature of pattern +matching used here is the *wildcard*, written `_`, which is +a pattern matching any value, without giving it a name. + +We did not explore the whole power of pattern matching yet, but we +will stop here in order to keep this document short. We still want to +see how the two functions above perform on a real example. For that +purpose, let's write a simple `main` function which performs +several operations on the expression `(x+x)+(7+y)`: it first computes +its value in the environment `{ x -> 5, y -> 7 }`, then +computes its derivative relative to `x` and then `y`. + + def main(args: Array[String]) { + val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) + val env: Environment = { case "x" => 5 case "y" => 7 } + println("Expression: " + exp) + println("Evaluation with x=5, y=7: " + eval(exp, env)) + println("Derivative relative to x:\n " + derive(exp, "x")) + println("Derivative relative to y:\n " + derive(exp, "y")) + } + +Executing this program, we get the expected output: + + Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) + Evaluation with x=5, y=7: 24 + Derivative relative to x: + Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0))) + Derivative relative to y: + Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) + +By examining the output, we see that the result of the derivative +should be simplified before being presented to the user. Defining a +basic simplification function using pattern matching is an interesting +(but surprisingly tricky) problem, left as an exercise for the reader. + +## Traits + +Apart from inheriting code from a super-class, a Scala class can also +import code from one or several *traits*. + +Maybe the easiest way for a Java programmer to understand what traits +are is to view them as interfaces which can also contain code. In +Scala, when a class inherits from a trait, it implements that trait's +interface, and inherits all the code contained in the trait. + +To see the usefulness of traits, let's look at a classical example: +ordered objects. It is often useful to be able to compare objects of a +given class among themselves, for example to sort them. In Java, +objects which are comparable implement the `Comparable` +interface. In Scala, we can do a bit better than in Java by defining +our equivalent of `Comparable` as a trait, which we will call +`Ord`. + +When comparing objects, six different predicates can be useful: +smaller, smaller or equal, equal, not equal, greater or equal, and +greater. However, defining all of them is fastidious, especially since +four out of these six can be expressed using the remaining two. That +is, given the equal and smaller predicates (for example), one can +express the other ones. In Scala, all these observations can be +nicely captured by the following trait declaration: + + trait Ord { + def < (that: Any): Boolean + def <=(that: Any): Boolean = (this < that) || (this == that) + def > (that: Any): Boolean = !(this <= that) + def >=(that: Any): Boolean = !(this < that) + } + +This definition both creates a new type called `Ord`, which +plays the same role as Java's `Comparable` interface, and +default implementations of three predicates in terms of a fourth, +abstract one. The predicates for equality and inequality do not appear +here since they are by default present in all objects. + +The type `Any` which is used above is the type which is a +super-type of all other types in Scala. It can be seen as a more +general version of Java's `Object` type, since it is also a +super-type of basic types like `Int`, `Float`, etc. + +To make objects of a class comparable, it is therefore sufficient to +define the predicates which test equality and inferiority, and mix in +the `Ord` class above. As an example, let's define a +`Date` class representing dates in the Gregorian calendar. Such +dates are composed of a day, a month and a year, which we will all +represent as integers. We therefore start the definition of the +`Date` class as follows: + + 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 + +The important part here is the `extends Ord` declaration which +follows the class name and parameters. It declares that the +`Date` class inherits from the `Ord` trait. + +Then, we redefine the `equals` method, inherited from +`Object`, so that it correctly compares dates by comparing their +individual fields. The default implementation of `equals` is not +usable, because as in Java it compares objects physically. We arrive +at the following definition: + + override def equals(that: Any): Boolean = + that.isInstanceOf[Date] && { + val o = that.asInstanceOf[Date] + o.day == day && o.month == month && o.year == year + } + +This method makes use of the predefined methods `isInstanceOf` +and `asInstanceOf`. The first one, `isInstanceOf`, +corresponds to Java's `instanceof` operator, and returns true +if and only if the object on which it is applied is an instance of the +given type. The second one, `asInstanceOf`, corresponds to +Java's cast operator: if the object is an instance of the given type, +it is viewed as such, otherwise a `ClassCastException` is +thrown. + +Finally, the last method to define is the predicate which tests for +inferiority, as follows. It makes use of another predefined method, +`error`, which throws an exception with the given error message. + + def <(that: Any): Boolean = { + if (!that.isInstanceOf[Date]) + 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 can be seen either as dates or as comparable objects. +Moreover, they all define the six comparison predicates mentioned +above: `equals` and `<` because they appear directly in +the definition of the `Date` class, and the others because they +are inherited from the `Ord` trait. + +Traits are useful in other situations than the one shown here, of +course, but discussing their applications in length is outside the +scope of this document. + +## Genericity + +The last characteristic of Scala we will explore in this tutorial is +genericity. Java programmers should be well aware of the problems +posed by the lack of genericity in their language, a shortcoming which +is addressed in Java 1.5. + +Genericity is the ability to write code parametrized by types. For +example, a programmer writing a library for linked lists faces the +problem of deciding which type to give to the elements of the list. +Since this list is meant to be used in many different contexts, it is +not possible to decide that the type of the elements has to be, say, +`Int`. This would be completely arbitrary and overly +restrictive. + +Java programmers resort to using `Object`, which is the +super-type of all objects. This solution is however far from being +ideal, since it doesn't work for basic types (`int`, +`long`, `float`, etc.) and it implies that a lot of +dynamic type casts have to be inserted by the programmer. + +Scala makes it possible to define generic classes (and methods) to +solve this problem. Let us examine this with an example of the +simplest container class possible: a reference, which can either be +empty or point to an object of some type. + + class Reference[T] { + private var contents: T = _ + def set(value: T) { contents = value } + def get: T = contents + } + +The class `Reference` is parametrized by a type, called `T`, +which is the type of its element. This type is used in the body of the +class as the type of the `contents` variable, the argument of +the `set` method, and the return type of the `get` method. + +The above code sample introduces variables in Scala, which should not +require further explanations. It is however interesting to see that +the initial value given to that variable is `_`, which represents +a default value. This default value is 0 for numeric types, +`false` for the `Boolean` type, `()` for the `Unit` +type and `null` for all object types. + +To use this `Reference` class, one needs to specify which type to use +for the type parameter `T`, that is the type of the element +contained by the cell. For example, to create and use a cell holding +an integer, one could write the following: + + object IntegerReference { + def main(args: Array[String]) { + val cell = new Reference[Int] + cell.set(13) + println("Reference contains the half of " + (cell.get * 2)) + } + } + +As can be seen in that example, it is not necessary to cast the value +returned by the `get` method before using it as an integer. It +is also not possible to store anything but an integer in that +particular cell, since it was declared as holding an integer. + +## Conclusion + +This document gave a quick overview of the Scala language and +presented some basic examples. The interested reader can go on, for example, by +reading the document *Scala By Example*, which +contains much more advanced examples, and consult the *Scala + Language Specification* when needed. From f919ef45861bf060037dc7ec9ed725f4ec9469ed Mon Sep 17 00:00:00 2001 From: Christian Krause Date: Tue, 1 May 2012 20:37:28 +0200 Subject: [PATCH 2/6] java-tutorial: singletons --- de/tutorials/scala-for-java-programmers.md | 25 ++++++++++------------ 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/de/tutorials/scala-for-java-programmers.md b/de/tutorials/scala-for-java-programmers.md index 49b7506c4c..15455898ea 100644 --- a/de/tutorials/scala-for-java-programmers.md +++ b/de/tutorials/scala-for-java-programmers.md @@ -34,22 +34,19 @@ Die Struktur des Programmes sollte Java Anwendern bekannt vorkommen: es besteht namens `main`, welche die Kommandozeilenparameter als Feld (Array) von Zeichenketten (String) übergeben bekommt. Der Körper dieser Methode besteht aus einem einzelnen Aufruf der vordefinierten Methode `println`, die die freundliche Begrüßung als Parameter übergeben bekommt. Weiterhin hat die -`main`-Methode keinen Rückgabewert, sie ist also eine Prozedur. Daher ist es auch nicht notwendig, +Methode `main` keinen Rückgabewert, sie ist also eine Prozedur. Daher ist es auch nicht notwendig, einen Rückgabetyp zu deklarieren. -What is less familiar to Java programmers is the `object` -declaration containing the `main` method. Such a declaration -introduces what is commonly known as a *singleton object*, that -is a class with a single instance. The declaration above thus declares -both a class called `HelloWorld` and an instance of that class, -also called `HelloWorld`. This instance is created on demand, -the first time it is used. - -The astute reader might have noticed that the `main` method is -not declared as `static` here. This is because static members -(methods or fields) do not exist in Scala. Rather than defining static -members, the Scala programmer declares these members in singleton -objects. +Was Java Programmierern allerdings weniger bekannt sein sollte, ist die Deklaration `object`, welche +die Methode `main` enthält. Eine solche Deklaration stellt dar, was gemeinhin als *Singleton Objekt* +(Einzelstück) bekannt ist, also eine Klasse mit nur einer Instanz. Im Beispiel oben werden also mit +dem Schlüsselwort `object` sowohl eine Klasse namens `HalloWelt` als auch die dazugehörige, +gleichnamige Instanz deklariert. Diese Instanz wird erst bei ihrer erstmaligen Verwendung erstellt. + +Dem aufmerksamen Leser wird vielleicht aufgefallen sein, dass die Methode `main` nicht als `static` +deklariert wurde. Der Grund dafür ist, dass statische Mitglieder (Attribute oder Methoden) in Scala +nicht existieren. Statt statische Mitglieder zu definieren deklariert ein Scala Programmierer diese +Mitglieder in Singleton Objekten. ### Compiling the example From 6780535c9032e26e23cb64910bf2d70add85c4e6 Mon Sep 17 00:00:00 2001 From: Christian Krause Date: Tue, 1 May 2012 21:04:01 +0200 Subject: [PATCH 3/6] java-tutorial: compiling and running --- de/tutorials/scala-for-java-programmers.md | 41 ++++++++++------------ 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/de/tutorials/scala-for-java-programmers.md b/de/tutorials/scala-for-java-programmers.md index 15455898ea..376b5a00f5 100644 --- a/de/tutorials/scala-for-java-programmers.md +++ b/de/tutorials/scala-for-java-programmers.md @@ -48,36 +48,33 @@ deklariert wurde. Der Grund dafür ist, dass statische Mitglieder (Attribute ode nicht existieren. Statt statische Mitglieder zu definieren deklariert ein Scala Programmierer diese Mitglieder in Singleton Objekten. -### Compiling the example +### Das Beispiel kompilieren -To compile the example, we use `scalac`, the Scala compiler. `scalac` -works like most compilers: it takes a source file as argument, maybe -some options, and produces one or several object files. The object -files it produces are standard Java class files. +Um das obige Beispiel zu kompilieren, verwenden wir `scalac`, den Scala-Compiler. `scalac` arbeitet +wie die meisten anderen Compiler auch: er akzeptiert Quelltext-Dateien als Parameter, einige weitere +Optionen, und übersetzt den Quelltext in Java-Bytecode. Dieser Bytecode wird in ein oder mehrere +Java-konforme Class-Dateien (mit der Endung `.class`) geschrieben. -If we save the above program in a file called -`HelloWorld.scala`, we can compile it by issuing the following -command (the greater-than sign `>` represents the shell prompt -and should not be typed): +Wenn wir obigen Quelltext in eine Datei namens `HalloWelt.scala` schreiben, können wir diese mit dem +folgenden Befehl kompilieren (das größer als Zeichen `>` repräsentiert die Eingabeaufforderung und +sollte nicht mit geschrieben werden): - > scalac HelloWorld.scala + > scalac HalloWelt.scala -This will generate a few class files in the current directory. One of -them will be called `HelloWorld.class`, and contains a class -which can be directly executed using the `scala` command, as the -following section shows. +Damit werden ein paar Class-Dateien in das aktuelle Verzeichnis geschrieben. Eine davon heißt +`HalloWelt.class` und enthält die Klasse, die direkt mit dem Befehl `scala` ausgeführt werden kann, +was in den folgenden Abschnitten erklärt wird. -### Running the example +### Das Beispiel ausführen -Once compiled, a Scala program can be run using the `scala` command. -Its usage is very similar to the `java` command used to run Java -programs, and accepts the same options. The above example can be -executed using the following command, which produces the expected -output: +Sobald kompiliert, kann ein Scala Programm mit dem Befehl `scala` ausgeführt werden. Die Anwendung +ist dem Befehl `java`, mit dem man Java Programme ausführt, sehr ähnlich und akzeptiert die selben +Optionen. Das obige Beispiel können wir demnach mit folgendem Befehl ausführen, was das erwartete +Resultat ausgibt: - > scala -classpath . HelloWorld + > scala -classpath . HalloWelt - Hello, world! + Hallo, Welt! ## Interaction with Java From 50d9cf5c3430ef79b4be27d028e03e64ffc35a8b Mon Sep 17 00:00:00 2001 From: Christian Krause Date: Thu, 3 May 2012 23:05:51 +0200 Subject: [PATCH 4/6] everythings an object --- de/tutorials/scala-for-java-programmers.md | 184 ++++++++++----------- 1 file changed, 83 insertions(+), 101 deletions(-) diff --git a/de/tutorials/scala-for-java-programmers.md b/de/tutorials/scala-for-java-programmers.md index 376b5a00f5..2d6f0478f8 100644 --- a/de/tutorials/scala-for-java-programmers.md +++ b/de/tutorials/scala-for-java-programmers.md @@ -20,9 +20,9 @@ insbesondere Java, werden vorausgesetzt. ## Das erste Beispiel -Als erstes werden wir das wohlbekannte *Hallo, Welt!*-Programm implementieren. Obwohl es sehr +Als erstes folgt eine Implementierung des wohlbekannten *Hallo, Welt!*-Programmes. Obwohl es sehr einfach ist, eignet es sich sehr gut, Scala's Funktionsweise zu demonstrieren, ohne dass man viel -über die Sprache wissen muss. So sieht es aus: +über die Sprache wissen muss. object HalloWelt { def main(args: Array[String]) { @@ -50,12 +50,12 @@ Mitglieder in Singleton Objekten. ### Das Beispiel kompilieren -Um das obige Beispiel zu kompilieren, verwenden wir `scalac`, den Scala-Compiler. `scalac` arbeitet -wie die meisten anderen Compiler auch: er akzeptiert Quelltext-Dateien als Parameter, einige weitere -Optionen, und übersetzt den Quelltext in Java-Bytecode. Dieser Bytecode wird in ein oder mehrere +Um das obige Beispiel zu kompilieren, wird `scalac`, der Scala-Compiler verwendet. `scalac` arbeitet +wie die meisten anderen Compiler auch: er akzeptiert Quellcode-Dateien als Parameter, einige weitere +Optionen, und übersetzt den Quellcode in Java-Bytecode. Dieser Bytecode wird in ein oder mehrere Java-konforme Class-Dateien (mit der Endung `.class`) geschrieben. -Wenn wir obigen Quelltext in eine Datei namens `HalloWelt.scala` schreiben, können wir diese mit dem +Schreibt man den obigen Quellcode in eine Datei namens `HalloWelt.scala`, kann man diese mit dem folgenden Befehl kompilieren (das größer als Zeichen `>` repräsentiert die Eingabeaufforderung und sollte nicht mit geschrieben werden): @@ -68,30 +68,29 @@ was in den folgenden Abschnitten erklärt wird. ### Das Beispiel ausführen Sobald kompiliert, kann ein Scala Programm mit dem Befehl `scala` ausgeführt werden. Die Anwendung -ist dem Befehl `java`, mit dem man Java Programme ausführt, sehr ähnlich und akzeptiert die selben -Optionen. Das obige Beispiel können wir demnach mit folgendem Befehl ausführen, was das erwartete +ist dem Befehl `java`, mit dem man Java Programme ausführt, nachempfunden und akzeptiert die selben +Optionen. Das obige Beispiel kann demnach mit folgendem Befehl ausgeführt werden, was das erwartete Resultat ausgibt: > scala -classpath . HalloWelt Hallo, Welt! -## Interaction with Java +## Interaktion mit Java -One of Scala's strengths is that it makes it very easy to interact -with Java code. All classes from the `java.lang` package are -imported by default, while others need to be imported explicitly. +Eine Stärke der Sprache Scala ist, dass man mit ihr sehr leicht mit Java interagieren kann. Alle +Klassen des Paketes `java.lang` werden automatisch importiert, während andere explizit importiert +werden müssen. -Let's look at an example that demonstrates this. We want to obtain -and format the current date according to the conventions used in a -specific country, say France. (Other regions such as the -French-speaking part of Switzerland use the same conventions.) +Als nächstes folgt ein Beispiel, was diese Interoperabilität demonstriert. Ziel ist es, das aktuelle +Datum zu erhalten und gemäß den Konventionen eines gewissen Landes zu formatieren, zum Beispiel +Frankreich (andere Regionen, wie der französisch-sprachige Teil der Schweiz verwenden dieselben +Konventionen). -Java's class libraries define powerful utility classes, such as -`Date` and `DateFormat`. Since Scala interoperates -seemlessly with Java, there is no need to implement equivalent -classes in the Scala class library--we can simply import the classes -of the corresponding Java packages: +Javas Klassen-Bibliothek enthält viele nützliche Klassen, beispielsweise `Date` und `DateFormat`. +Dank Scala's Fähigkeit, nahtlos mit Java zu interoperieren, besteht keine Notwendigkeit, äquivalente +Klassen in der Scala Klassen-Bibliothek zu implementieren--man kann einfach die entsprechenden +Klassen der Java-Pakete importieren: import java.util.{Date, Locale} import java.text.DateFormat @@ -105,121 +104,104 @@ of the corresponding Java packages: } } -Scala's import statement looks very similar to Java's equivalent, -however, it is more powerful. Multiple classes can be imported from -the same package by enclosing them in curly braces as on the first -line. Another difference is that when importing all the names of a -package or class, one uses the underscore character (`_`) instead -of the asterisk (`*`). That's because the asterisk is a valid -Scala identifier (e.g. method name), as we will see later. - -The import statement on the third line therefore imports all members -of the `DateFormat` class. This makes the static method -`getDateInstance` and the static field `LONG` directly -visible. - -Inside the `main` method we first create an instance of Java's -`Date` class which by default contains the current date. Next, we -define a date format using the static `getDateInstance` method -that we imported previously. Finally, we print the current date -formatted according to the localized `DateFormat` instance. This -last line shows an interesting property of Scala's syntax. Methods -taking one argument can be used with an infix syntax. That is, the -expression +Scala's Import-Anweisung ähnelt sehr der aus Java, obwohl sie viel mächtiger ist. Mehrere Klassen +des gleichen Paketes können gleichzeitig importiert werden, indem sie, wie in der ersten Zeile, in +geschweifte Klammern geschrieben werden. Ein weiterer Unterschied ist, dass, wenn man alle +Mitglieder eines Paketes importieren will, einen Unterstrich (`_`) anstelle des Asterisk (`*`) +verwendet. Das liegt daran, dass der Asterisk ein gültiger Bezeichner in Scala ist, beispielsweise +als Name für Methoden, wie später gezeigt wird. Die Import-Anweisung der dritten Zeile importiert +demnach alle Mitglieder der Klasse `DateFormat`, inklusive der statischen Methode `getDateInstance` +und des statischen Feldes `LONG`. + +Innerhalb der Methode `main` wird zuerst eine Instanz der Java-Klasse `Date` erzeugt, welche, per +Default, das aktuelle Datum enthält. Als nächstes wird mithilfe der statischen Methode +`getDateInstance` eine Instanz der Klasse `DateFormat` erstellt. Schließlich wird das aktuelle Datum +gemäß der Regeln der lokalisierten `DateFormat`-Instanz formatiert ausgegeben. Außerdem +veranschaulicht die letzte Zeile eine interessante Fähigkeit Scala's Syntax: Methoden, die nur einen +Parameter haben, können in der Infix-Syntax notiert werden. Dies bedeutet, dass der Ausdruck df format now -is just another, slightly less verbose way of writing the expression +eine andere, weniger verbose Variante des folgenden Ausdruckes ist: df.format(now) -This might seem like a minor syntactic detail, but it has important -consequences, one of which will be explored in the next section. +Es scheint nur ein nebensächliches, syntaktisches Detail zu sein, hat jedoch bedeutende +Konsequenzen, was im nächsten Abschnitt gezeigt wird. Um diesen Abschnitt abzuschließen, soll +bemerkt werden, dass es außerdem direkt in Scala möglich ist, von Java-Klassen zu erben +Java-Schnittstellen zu implementieren. -To conclude this section about integration with Java, it should be -noted that it is also possible to inherit from Java classes and -implement Java interfaces directly in Scala. +## Alles ist ein Objekt -## Everything is an Object +Scala ist eine pur Objekt-orientierte Sprache, in dem Sinne dass *alles* ein Objekt ist, Zahlen und +Funktionen eingeschlossen. Der Unterschied zu Java ist, dass Java zwischen primitiven Typen, wie +`boolean` und `int`, und den Referenz-Typen unterscheidet und es nicht erlaubt ist, Funktionen wie +Werte zu behandeln. -Scala is a pure object-oriented language in the sense that -*everything* is an object, including numbers or functions. It -differs from Java in that respect, since Java distinguishes -primitive types (such as `boolean` and `int`) from reference -types, and does not enable one to manipulate functions as values. +### Zahlen sind Objekte -### Numbers are objects - -Since numbers are objects, they also have methods. And in fact, an -arithmetic expression like the following: +Zahlen sind Objekte und haben daher Methoden. Tatsächlich besteht ein arithmetischer Ausdruck wie +der folgende: 1 + 2 * 3 / x -consists exclusively of method calls, because it is equivalent to the -following expression, as we saw in the previous section: +exklusiv aus Methoden-Aufrufen, da es äquivalent zu folgendem Ausdruck ist, wie in vorhergehenden +Abschnitt gezeigt wurde: (1).+(((2).*(3))./(x)) -This also means that `+`, `*`, etc. are valid identifiers -in Scala. +Dies bedeutet außerdem, dass `+`, `*`, etc. in Scala gültige Bezeichner sind. -The parentheses around the numbers in the second version are necessary -because Scala's lexer uses a longest match rule for tokens. -Therefore, it would break the following expression: +Die Zahlen umschließenden Klammern der zweiten Variante sind notwendig, weil Scala's lexikalischer +Scanner eine Regel zur längsten Übereinstimmung der Token verwendet. Daher würde der folgende +Ausdruck: 1.+(2) -into the tokens `1.`, `+`, and `2`. The reason that -this tokenization is chosen is because `1.` is a longer valid -match than `1`. The token `1.` is interpreted as the -literal `1.0`, making it a `Double` rather than an -`Int`. Writing the expression as: +in die Token `1.`, `+`, und `2` zerlegt werden. Der Grund für diese Zerlegung ist, dass `1.` ein +längere, gültige Übereinstimmung ist, als `1`. Daher wird das Token `1.` als das Literal `1.0` +interpretiert, also als Gleitkomma- statt als Ganzzahl. Den Ausdruck als (1).+(2) -prevents `1` from being interpreted as a `Double`. - -### Functions are objects - -Perhaps more surprising for the Java programmer, functions are also -objects in Scala. It is therefore possible to pass functions as -arguments, to store them in variables, and to return them from other -functions. This ability to manipulate functions as values is one of -the cornerstone of a very interesting programming paradigm called -*functional programming*. - -As a very simple example of why it can be useful to use functions as -values, let's consider a timer function whose aim is to perform some -action every second. How do we pass it the action to perform? Quite -logically, as a function. This very simple kind of function passing -should be familiar to many programmers: it is often used in -user-interface code, to register call-back functions which get called -when some event occurs. - -In the following program, the timer function is called -`oncePerSecond`, and it gets a call-back function as argument. -The type of this function is written `() => Unit` and is the type -of all functions which take no arguments and return nothing (the type -`Unit` is similar to `void` in C/C++). The main function of -this program simply calls this timer function with a call-back which -prints a sentence on the terminal. In other words, this program -endlessly prints the sentence "time flies like an arrow" every -second. +zu schreiben, verhindert also, dass `1` als Gleitkommazahl interpretiert wird. + +### Funktionen sind Objekte + +Vermutlich überraschender für Java Programmierer ist, dass auch Funktionen in Scala Objekte sind. +Daher ist es auch möglich, Funktionen als Parameter zu übergeben, als Werte zu speichern, und von +anderen Funktionen zurück geben zu lassen. Diese Fähigkeit, Funktionen wie Werte zu behandeln, ist +einer der Grundsteine eines sehr interessanten Programmier-Paradigmas, der *funktionalen +Programmierung*. + +Ein sehr einfaches Beispiel, warum es nützlich sein kann, Funktionen wie Werte zu behandeln, ist +eine Timer-Funktion, deren Ziel es ist, eine gewisse Aktion pro Sekunde durchzuführen. Wie übergibt +man die durchzuführende Aktion? Offensichtlich als Funktion. Diese einfache Art der Übergabe einer +Funktion sollte den meisten Programmieren bekannt vorkommen: dieses Prinzip wird häufig bei +Benutzerschnittstellen für Rückruf-Funktionen (call-back) verwendet, die ausgeführt werden, wenn ein +bestimmtes Ereignis eintritt. + +Im folgenden Programm akzeptiert die Timer-Funktion `oncePerSecond` eine Rückruf-Funktion als +Parameter. Der Typ wird `() => Unit` geschrieben und ist der Typ aller Funktionen, die keine +Parameter haben und nichts zurück geben (der Typ `Unit` ist das Äquivalent zu `void` in C/C++ und +Java). Die Methode `main` des Programmes ruft die Timer-Funktion mit der Rückruf-Funktion auf, die +einen Satz ausgibt. In anderen Worten: das Programm gibt endlos den Satz "Die Zeit vergeht wie im +Flug." einmal pro Sekunde aus. object Timer { def oncePerSecond(callback: () => Unit) { while (true) { callback(); Thread sleep 1000 } } def timeFlies() { - println("time flies like an arrow...") + println("Die Zeit vergeht wie im Flug.") } def main(args: Array[String]) { oncePerSecond(timeFlies) } } -Note that in order to print the string, we used the predefined method -`println` instead of using the one from `System.out`. +Zu bemerken ist, dass um die Zeichenkette auszugeben, die in Scala vordefinierte Methode `println` +statt der äquivalenten Methode in `System.out` verwendet wird. #### Anonymous functions From 31b747db2e8904cdca7c94fa9d1d72c294df29d7 Mon Sep 17 00:00:00 2001 From: Christian Krause Date: Mon, 14 May 2012 00:25:29 +0200 Subject: [PATCH 5/6] fully translated --- de/tutorials/scala-for-java-programmers.md | 655 +++++++++------------ 1 file changed, 289 insertions(+), 366 deletions(-) diff --git a/de/tutorials/scala-for-java-programmers.md b/de/tutorials/scala-for-java-programmers.md index 2d6f0478f8..9e2a508b53 100644 --- a/de/tutorials/scala-for-java-programmers.md +++ b/de/tutorials/scala-for-java-programmers.md @@ -203,78 +203,65 @@ Flug." einmal pro Sekunde aus. Zu bemerken ist, dass um die Zeichenkette auszugeben, die in Scala vordefinierte Methode `println` statt der äquivalenten Methode in `System.out` verwendet wird. -#### Anonymous functions - -While this program is easy to understand, it can be refined a bit. -First of all, notice that the function `timeFlies` is only -defined in order to be passed later to the `oncePerSecond` -function. Having to name that function, which is only used once, might -seem unnecessary, and it would in fact be nice to be able to construct -this function just as it is passed to `oncePerSecond`. This is -possible in Scala using *anonymous functions*, which are exactly -that: functions without a name. The revised version of our timer -program using an anonymous function instead of *timeFlies* looks -like that: +#### Anonyme Funktionen + +Während das obige Programm schon leicht zu verstehen ist, kann es noch modifiziert werden. Als +erstes ist zu bemerken, dass die Funktion `timeFlies` nur definiert wurde, um der Funktion +`oncePerSecond` als Parameter übergeben zu werden. Dieser nur einmal verwendeten Funktion einen +Namen zu geben, scheint unnötig und es wäre angenehmer, sie direkt mit der Übergabe zu erstellen. +Das ist in Scala mit *anonymen Funktionen* möglich, die genau das darstellen: eine Funktion ohne +Namen. Die überarbeitete Version des obigen Timer-Programmes verwendet eine anonyme Funktion anstatt +der Funktion `timeFlies`: object TimerAnonymous { def oncePerSecond(callback: () => Unit) { while (true) { callback(); Thread sleep 1000 } } def main(args: Array[String]) { - oncePerSecond(() => - println("time flies like an arrow...")) + oncePerSecond(() => println("Die Zeit vergeht wie im Flug.")) } } -The presence of an anonymous function in this example is revealed by -the right arrow `=>` which separates the function's argument -list from its body. In this example, the argument list is empty, as -witnessed by the empty pair of parenthesis on the left of the arrow. -The body of the function is the same as the one of `timeFlies` -above. +Die anonyme Funktion erkennt man an dem Rechtspfeil `=>`, der die Parameter vom Körper der Funktion +trennt. In diesem Beispiel ist die Liste der Parameter leer, wie man an den leeren Klammern erkennen +kann. Der Körper der Funktion ist derselbe, wie bei der `timeFlies` Funktion des vorangegangenen +Beispiels. -## Classes +## Klassen -As we have seen above, Scala is an object-oriented language, and as -such it has a concept of class. (For the sake of completeness, - it should be noted that some object-oriented languages do not have - the concept of class, but Scala is not one of them.) -Classes in Scala are declared using a syntax which is close to -Java's syntax. One important difference is that classes in Scala can -have parameters. This is illustrated in the following definition of -complex numbers. +Wie oben zu sehen war, ist Scala eine Objekt-orientierte Sprache, und als solche enthält sie das +Konzept von Klassen (der Vollständigkeit halber soll bemerkt sein, dass nicht alle +Objekt-orientierte Sprachen das Konzept von Klassen unterstützen, aber Scala ist keine von denen). +Klassen in Scala werden mit einer ähnlichen Syntax wie Java deklariert. Ein wichtiger Unterschied +ist, dass Scala's Klassen Argumente haben. Dies wird mit der folgenden Definition von komplexen +Zahlen veranschaulicht: class Complex(real: Double, imaginary: Double) { def re() = real def im() = imaginary } -This complex class takes two arguments, which are the real and -imaginary part of the complex. These arguments must be passed when -creating an instance of class `Complex`, as follows: `new - Complex(1.5, 2.3)`. The class contains two methods, called `re` -and `im`, which give access to these two parts. - -It should be noted that the return type of these two methods is not -given explicitly. It will be inferred automatically by the compiler, -which looks at the right-hand side of these methods and deduces that -both return a value of type `Double`. - -The compiler is not always able to infer types like it does here, and -there is unfortunately no simple rule to know exactly when it will be, -and when not. In practice, this is usually not a problem since the -compiler complains when it is not able to infer a type which was not -given explicitly. As a simple rule, beginner Scala programmers should -try to omit type declarations which seem to be easy to deduce from the -context, and see if the compiler agrees. After some time, the -programmer should get a good feeling about when to omit types, and -when to specify them explicitly. - -### Methods without arguments - -A small problem of the methods `re` and `im` is that, in -order to call them, one has to put an empty pair of parenthesis after -their name, as the following example shows: +Diese Klasse akzeptiert zwei Argumente, den realen und den imaginären Teil der komplexen Zahl. Sie +müssen beim kreieren einer Instanz der Klasse übergeben werden: + + val c = new Complex(1.5, 2.3) + +Weiterhin enthält die Klasse zwei Methoden, `re` und `im`, welche als Zugriffsfunktionen (Getter) +dienen. Außerdem soll bemerkt sein, dass der Rückgabewert dieser Methoden nicht explizit deklariert +ist. Der Compiler schlussfolgert automatisch, indem er ihn aus dem rechten Teil der Methoden +ableitet, dass der Rückgabewert vom Typ `Double` ist. + +Der Compiler ist nicht immer fähig, auf den Rückgabewert zu schließen, und es gibt leider keine +einfache Regel, um zu wissen, dies vorauszusagen. In der Praxis ist das üblicherweise kein Problem, +da der Compiler sich beschwert, wenn es ihm nicht möglich ist. Als einfache Regel sollten +Scala-Anfänger versuchen, Typ-Deklarationen, die leicht vom Kontext abzuleiten sind, wegzulassen, um +zu sehen, ob der Compiler zustimmt. Nach einer gewissen Zeit, bekommt man ein Gefühl dafür, wann man +auf diese Deklarationen verzichten kann und wann man sie explizit angeben sollte. + +### Methoden ohne Argumente + +Ein Problem der obigen Methoden `re` und `im` ist, dass man, um sie zu verwenden, ein Paar leerer +Klammern hinter ihren Namen anhängen muss: object ComplexNumbers { def main(args: Array[String]) { @@ -283,32 +270,26 @@ their name, as the following example shows: } } -It would be nicer to be able to access the real and imaginary parts -like if they were fields, without putting the empty pair of -parenthesis. This is perfectly doable in Scala, simply by defining -them as methods *without arguments*. Such methods differ from -methods with zero arguments in that they don't have parenthesis after -their name, neither in their definition nor in their use. Our -`Complex` class can be rewritten as follows: +Besser wäre jedoch, wenn man den realen und imaginären Teil so abrufen könnte, als wären sie Felder, +also ohne das leere Klammerpaar. Mit Scala ist das möglich, indem Methoden *ohne Argumente* +definiert werden. Solche Methoden haben keine Klammern nach ihrem Namen, weder bei ihrer Definition +noch bei ihrer Verwendung. Die Klasse für komplexe Zahlen kann demnach folgendermaßen umgeschrieben +werden: class Complex(real: Double, imaginary: Double) { def re = real def im = imaginary } +### Vererbung und Überschreiben -### Inheritance and overriding - -All classes in Scala inherit from a super-class. When no super-class -is specified, as in the `Complex` example of previous section, -`scala.AnyRef` is implicitly used. +Alle Klassen in Scala erben von einer Oberklasse. Wird keine Oberklasse spezifiziert, wie bei der +Klasse `Complex` des vorhergehenden Abschnittes, wird implizit `scala.AnyRef` verwendet. -It is possible to override methods inherited from a super-class in -Scala. It is however mandatory to explicitly specify that a method -overrides another one using the `override` modifier, in order to -avoid accidental overriding. As an example, our `Complex` class -can be augmented with a redefinition of the `toString` method -inherited from `Object`. +Es ist möglich, von einer Oberklasse vererbte Methoden zu überschreiben. Dabei muss jedoch explizit +das Schlüsselwort `override` angegeben werden, um versehentliche Überschreibungen zu vermeiden. Als +Beispiel soll eine Erweiterung der Klasse `Complex` dienen, die die Methode `toString` neu +definiert: class Complex(real: Double, imaginary: Double) { def re = real @@ -317,94 +298,76 @@ inherited from `Object`. "" + re + (if (im < 0) "" else "+") + im + "i" } +## Container-Klassen und Musterabgleich -## Case Classes and Pattern Matching +Eine Datenstruktur, die häufig in Programmen vorkommt ist der Baum, beispielsweise repräsentieren +Interpreter und Compiler Programme intern häufig als Bäume, XML-Dokumente sind Bäume und einige +Container basieren auf Bäumen, wie Rot-Schwarz-Bäume. -A kind of data structure that often appears in programs is the tree. -For example, interpreters and compilers usually represent programs -internally as trees; XML documents are trees; and several kinds of -containers are based on trees, like red-black trees. +Als nächstes wird anhand eines kleinen Programmes für Berechnungen gezeigt, wie solche Bäume in +Scala repräsentiert und manipuliert werden können. Das Ziel dieses Programmes ist, einfache +arithmetische Ausdrücke zu manipulieren, die aus Summen, Ganzzahlen und Variablen bestehen. +Beispiele solcher Ausdrücke sind: `1 + 2` und `(x+x) + (7+y)`. -We will now examine how such trees are represented and manipulated in -Scala through a small calculator program. The aim of this program is -to manipulate very simple arithmetic expressions composed of sums, -integer constants and variables. Two examples of such expressions are -`1+2` and `(x+x)+(7+y)`. +Dafür muss zuerst eine Repräsentation für die Ausdrücke gewählt werden. Die natürlichste ist ein +Baum, dessen Knoten Operationen (Additionen) und dessen Blätter Werte (Konstanten und Variablen) +darstellen. -We first have to decide on a representation for such expressions. The -most natural one is the tree, where nodes are operations (here, the -addition) and leaves are values (here constants or variables). - -In Java, such a tree would be represented using an abstract -super-class for the trees, and one concrete sub-class per node or -leaf. In a functional programming language, one would use an algebraic -data-type for the same purpose. Scala provides the concept of -*case classes* which is somewhat in between the two. Here is how -they can be used to define the type of the trees for our example: +In Java würde man solche Bäume am ehesten mithilfe einer abstrakten Oberklasse für den Baum und +konkreten Implementierungen für Knoten und Blätter repräsentieren. In einer funktionalen Sprache +würde man algebraische Datentypen mit dem gleichen Ziel verwenden. Scala unterstützt das Konzept +einer Container-Klasse (case class), die einen gewissen Mittelweg dazwischen darstellen. Der +folgenden Quelltext veranschaulicht deren Anwendung: abstract class Tree case class Sum(l: Tree, r: Tree) extends Tree case class Var(n: String) extends Tree case class Const(v: Int) extends Tree -The fact that classes `Sum`, `Var` and `Const` are -declared as case classes means that they differ from standard classes -in several respects: - -- the `new` keyword is not mandatory to create instances of - these classes (i.e., one can write `Const(5)` instead of - `new Const(5)`), -- getter functions are automatically defined for the constructor - parameters (i.e., it is possible to get the value of the `v` - constructor parameter of some instance `c` of class - `Const` just by writing `c.v`), -- default definitions for methods `equals` and - `hashCode` are provided, which work on the *structure* of - the instances and not on their identity, -- a default definition for method `toString` is provided, and - prints the value in a "source form" (e.g., the tree for expression - `x+1` prints as `Sum(Var(x),Const(1))`), -- instances of these classes can be decomposed through - *pattern matching* as we will see below. - -Now that we have defined the data-type to represent our arithmetic -expressions, we can start defining operations to manipulate them. We -will start with a function to evaluate an expression in some -*environment*. The aim of the environment is to give values to -variables. For example, the expression `x+1` evaluated in an -environment which associates the value `5` to variable `x`, written -`{ x -> 5 }`, gives `6` as result. - -We therefore have to find a way to represent environments. We could of -course use some associative data-structure like a hash table, but we -can also directly use functions! An environment is really nothing more -than a function which associates a value to a (variable) name. The -environment `{ x -> 5 }` given above can simply be written as -follows in Scala: +Die Tatsache, dass die Klassen `Sum`, `Var` und `Const` als Container-Klassen deklariert sind, +bedeutet, dass sie sich in einigen Gesichtspunkten von normalen Klassen unterscheiden: + +- das Schlüsselwort `new` ist nicht mehr notwendig, um Instanzen dieser Klassen zu erzeugen (man + kann also `Const(5)` statt `new Const(5)` schreiben) +- Zugriffsfunktionen werden automatisch anhand der Parameter des Konstruktors erstellt (man kann + den Wert `v` einer Instanz `c` der Klasse `Const` erhalten, indem man `c.v` schreibt) +- der Compiler fügt Container-Klassen automatisch Implementierungen der Methoden `equals` und + `hashCode` hinzu, die auf der *Struktur* der Klassen basieren, nicht deren Identität +- außerdem wird eine `toString` Methode bereitgestellt, die einen Wert in Form der Quelle darstellt + (der String-Wert des Baum-Ausdruckes `x+1` ist `Sum(Var(x),Const(1))`) +- Instanzen dieser Klassen können mithilfe von Musterabgleich zerlegt werden, wie weiter unten zu + sehen ist + +Da jetzt bekannt ist, wie die Datenstruktur der arithmetischen Ausdrücke repräsentiert wird, können +jetzt Operationen definiert werden, um diese zu manipulieren. Der Beginn dessen soll eine Funktion +darstellen, die Ausdrücke in einer bestimmten *Umgebung* auswertet. Das Ziel einer Umgebung ist es, +Variablen Werte zuzuweisen. Beispielsweise wird der Ausdruck `x+1` in der Umgebung, die der Variable +`x` den Wert `5` zuweist, geschrieben als `{ x -> 5 }`, mit dem Resultat `6` ausgewertet. + +Demnach muss ein Weg gefunden werden, solche Umgebungen auszudrücken. Dabei könnte man sich für eine +assoziative Datenstruktur entscheiden, wie eine Hash-Tabelle, man könnte jedoch auch direkt eine +Funktion verwenden. Eine Umgebung ist nicht mehr als eine Funktion, die Werte mit Variablen +assoziiert. Die obige Umgebung `{ x -> 5 }` wird in Scala folgendermaßen notiert: { case "x" => 5 } -This notation defines a function which, when given the string -`"x"` as argument, returns the integer `5`, and fails with an -exception otherwise. +Diese Schreibweise definiert eine Funktion, welche bei dem String `"x"` als Argument die Ganzzahl +`5` zurückgibt, und in anderen Fällen mit einer Ausnahme fehlschlägt. -Before writing the evaluation function, let us give a name to the type -of the environments. We could of course always use the type -`String => Int` for environments, but it simplifies the program -if we introduce a name for this type, and makes future changes easier. -This is accomplished in Scala with the following notation: +Vor dem Schreiben der Funktionen zum Auswerten ist es sinnvoll, für die Umgebungen einen eigenen Typ +zu definieren. Man könnte zwar immer `String => Int` verwenden, es wäre jedoch besser einen +dedizierten Namen dafür zu verwenden, der das Programmieren damit einfacher macht. Dies wird in +Scala mit der folgenden Schreibweise erreicht: type Environment = String => Int -From then on, the type `Environment` can be used as an alias of -the type of functions from `String` to `Int`. +Von hier an wird `Environment` als Alias für den Typ von Funktionen von `String` nach `Int` +verwendet. -We can now give the definition of the evaluation function. -Conceptually, it is very simple: the value of a sum of two expressions -is simply the sum of the value of these expressions; the value of a -variable is obtained directly from the environment; and the value of a -constant is the constant itself. Expressing this in Scala is not more -difficult: +Nun ist alles für die Definition der Funktion zur Auswertung vorbereitet. Konzeptionell ist die +Definition sehr einfach: der Wert der Summe zweier Ausdrücke ist die Summe der Werte der +einzelnen Ausdrücke, der Wert einer Variable wird direkt der Umgebung entnommen und der Wert einer +Konstante ist die Konstante selbst. Dies in Scala auszudrücken ist nicht viel komplizierter: def eval(t: Tree, env: Environment): Int = t match { case Sum(l, r) => eval(l, env) + eval(r, env) @@ -412,64 +375,52 @@ difficult: case Const(v) => v } -This evaluation function works by performing *pattern matching* -on the tree `t`. Intuitively, the meaning of the above definition -should be clear: - -1. it first checks if the tree `t` is a `Sum`, and if it - is, it binds the left sub-tree to a new variable called `l` and - the right sub-tree to a variable called `r`, and then proceeds - with the evaluation of the expression following the arrow; this - expression can (and does) make use of the variables bound by the - pattern appearing on the left of the arrow, i.e., `l` and - `r`, -2. if the first check does not succeed, that is, if the tree is not - a `Sum`, it goes on and checks if `t` is a `Var`; if - it is, it binds the name contained in the `Var` node to a - variable `n` and proceeds with the right-hand expression, -3. if the second check also fails, that is if `t` is neither a - `Sum` nor a `Var`, it checks if it is a `Const`, and - if it is, it binds the value contained in the `Const` node to a - variable `v` and proceeds with the right-hand side, -4. finally, if all checks fail, an exception is raised to signal - the failure of the pattern matching expression; this could happen - here only if more sub-classes of `Tree` were declared. - -We see that the basic idea of pattern matching is to attempt to match -a value to a series of patterns, and as soon as a pattern matches, -extract and name various parts of the value, to finally evaluate some -code which typically makes use of these named parts. - -A seasoned object-oriented programmer might wonder why we did not -define `eval` as a *method* of class `Tree` and its -subclasses. We could have done it actually, since Scala allows method -definitions in case classes just like in normal classes. Deciding -whether to use pattern matching or methods is therefore a matter of -taste, but it also has important implications on extensibility: - -- when using methods, it is easy to add a new kind of node as this - can be done just by defining a sub-class of `Tree` for it; on - the other hand, adding a new operation to manipulate the tree is - tedious, as it requires modifications to all sub-classes of - `Tree`, -- when using pattern matching, the situation is reversed: adding a - new kind of node requires the modification of all functions which do - pattern matching on the tree, to take the new node into account; on - the other hand, adding a new operation is easy, by just defining it - as an independent function. - -To explore pattern matching further, let us define another operation -on arithmetic expressions: symbolic derivation. The reader might -remember the following rules regarding this operation: - -1. the derivative of a sum is the sum of the derivatives, -2. the derivative of some variable `v` is one if `v` is the - variable relative to which the derivation takes place, and zero - otherwise, -3. the derivative of a constant is zero. - -These rules can be translated almost literally into Scala code, to -obtain the following definition: +Diese Funktion zum Auswerten von arithmetischen Ausdrücken nutzt *Musterabgleich* (pattern +matching) anhand des Baumes `t`. Intuitiv sollte die Bedeutung der einzelnen Fälle klar sein: + +1. Als erstes wird überprüft, ob `t` eine Instanz der Klasse `Sum` ist. Falls dem so ist, wird der +linke Teilbaum der Variablen `l` und der rechte Teilbaum der Variablen `r` zugewiesen. Daraufhin +wird der Ausdruck auf der rechten Seite des Pfeiles ausgewertet, der die auf der linken Seite +gebundenen Variablen `l` und `r` verwendet. + +2. Sollte die erste Überprüfung fehlschlagen, also `t` ist keine `Sum`, wird der nächste Fall +abgehandelt und überprüft, ob `t` eine `Var` ist. Ist dies der Fall, wird analog zum ersten Fall der +Wert an `n` gebunden und der Ausdruck rechts vom Pfeil ausgewertet. + +3. Schlägt auch die zweite Überprüfung fehl, also `t` ist weder `Sum` noch `Val`, wird überprüft, +ob es eine Instanz des Typs `Const` ist. Analog wird bei einem Erfolg wie bei den beiden +vorangegangenen Fällen verfahren. + +4. Schließlich, sollten alle Überprüfungen fehlschlagen, wird eine Ausnahme ausgelöst, die +signalisiert, dass die Musterabgleich nicht erfolgreich war. Dies wird unweigerlich geschehen, +sollten neue Baum-Unterklassen erstellt werden. + +Die prinzipielle Idee eines Musterabgleich ist, einen Wert anhand einer Reihe von Mustern +abzugleichen und, sobald ein Treffer erzielt wird, Werte zu extrahieren, mit denen darauf +weitergearbeitet werden kann. + +Erfahrene Objekt-orientierte Programmierer werden sich fragen, warum `eval` nicht als Methode der +Klasse `Tree` oder dessen Unterklassen definiert wurde. Dies wäre möglich, da Container-Klassen +Methoden definieren können wie normale Klassen auch. Die Entscheidung, einen Musterabgleich oder +Methoden zu verwenden, ist Geschmackssache, hat jedoch wichtige Auswirkungen auf die +Erweiterbarkeit: + +- einerseits ist es mit Methoden einfach, neue Arten von Knoten als Unterklassen von `Tree` + hinzuzufügen, andererseits ist die Ergänzung einer neuen Operation zur Manipulation des Baumes + mühsam, da sie die Modifikation aller Unterklassen von `Tree` notwendig macht + +- nutzt man einen Musterabgleich kehrt sich die Situation um: eine neue Art von Knoten erfordert + die Modifikation aller Funktionen die einen Musterabgleich am Baum vollführen, wogegen eine neue + Operation leicht hinzuzufügen ist, indem einfach eine unabhängige Funktion dafür definiert wird + +Einen weiteren Einblick in Musterabgleiche verschafft eine weitere Operation mit arithmetischen +Ausdrücken: partielle Ableitungen. Dafür gelten zur Zeit folgende Regeln: + +1. die Ableitung einer Summe ist die Summe der Ableitungen +2. die Ableitung einer Variablen ist eins, wenn sie die abzuleitende Variable ist, ansonsten null +3. die Ableitung einer Konstanten ist null + +Diese Regeln können fast wörtlich in Scala übersetzt werden: def derive(t: Tree, v: String): Tree = t match { case Sum(l, r) => Sum(derive(l, v), derive(r, v)) @@ -477,72 +428,64 @@ obtain the following definition: case _ => Const(0) } -This function introduces two new concepts related to pattern matching. -First of all, the `case` expression for variables has a -*guard*, an expression following the `if` keyword. This -guard prevents pattern matching from succeeding unless its expression -is true. Here it is used to make sure that we return the constant `1` -only if the name of the variable being derived is the same as the -derivation variable `v`. The second new feature of pattern -matching used here is the *wildcard*, written `_`, which is -a pattern matching any value, without giving it a name. - -We did not explore the whole power of pattern matching yet, but we -will stop here in order to keep this document short. We still want to -see how the two functions above perform on a real example. For that -purpose, let's write a simple `main` function which performs -several operations on the expression `(x+x)+(7+y)`: it first computes -its value in the environment `{ x -> 5, y -> 7 }`, then -computes its derivative relative to `x` and then `y`. +Diese Funktion führt zwei neue, mit dem Musterabgleich zusammenhängende Konzepte ein. Der zweite, +sich auf eine Variable beziehende Fall hat eine *Sperre* (guard), einen Ausdruck, der dem +Schlüsselwort `if` folgt. Diese Sperre verhindert eine Übereinstimmung, wenn der Ausdruck falsch +ist. In diesem Fall wird sie genutzt, die Konstante `1` nur zurückzugeben, wenn die Variable die +abzuleitende ist. Die zweite Neuerung ist der *Platzhalter* `_`, der mit allem übereinstimmt, ohne +einen Namen dafür zu verwenden. + +Die volle Funktionalität von Musterabgleichen wurde mit diesen Beispielen nicht demonstriert, +doch soll dies fürs Erste genügen. Eine Vorführung der beiden Funktionen an realen Beispielen steht +immer noch aus. Zu diesem Zweck soll eine `main` Methode dienen, die den Ausdruck `(x+x)+(7+y)` als +Beispiel verwendet: zuerst wird der Wert in der Umgebung `{ x -> 5, y -> 7 }` und darauf die +beiden partiellen Ableitungen. def main(args: Array[String]) { val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) - val env: Environment = { case "x" => 5 case "y" => 7 } - println("Expression: " + exp) - println("Evaluation with x=5, y=7: " + eval(exp, env)) - println("Derivative relative to x:\n " + derive(exp, "x")) - println("Derivative relative to y:\n " + derive(exp, "y")) + val env: Environment = { + case "x" => 5 + case "y" => 7 + } + println("Ausdruck: " + exp) + println("Auswertung mit x=5, y=7: " + eval(exp, env)) + println("Ableitung von x:\n " + derive(exp, "x")) + println("Ableitung von y:\n " + derive(exp, "y")) } -Executing this program, we get the expected output: +Führt man das Programm aus, erhält man folgende Ausgabe: - Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) - Evaluation with x=5, y=7: 24 - Derivative relative to x: + Ausdruck: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) + Auswertung mit x=5, y=7: 24 + Ableitung von x: Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0))) - Derivative relative to y: + Ableitung von y: Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) -By examining the output, we see that the result of the derivative -should be simplified before being presented to the user. Defining a -basic simplification function using pattern matching is an interesting -(but surprisingly tricky) problem, left as an exercise for the reader. +Beim Anblick dieser Ausgabe ist offensichtlich, dass man die Ergebnisse der Ableitungen noch +vereinfachen sollte. Eine solche Funktion zum Vereinfachen von Ausdrücken, die Musterabgleiche nutzt +ist ein interessantes, aber gar nicht so einfaches Problem, was als Übung implementiert werden kann. ## Traits -Apart from inheriting code from a super-class, a Scala class can also -import code from one or several *traits*. +Neben dem Vererben von Oberklassen ist es in Scala auch möglich von mehreren, sogenannten *Traits* +zu erben. Der beste Weg für einen Java-Programmierer einen Trait zu verstehen, ist sich eine +Schnittstelle vorzustellen, die Implementierungen enthält. Wenn in Scala eine Klasse von einem Trait +erbt, implementiert sie dessen Schnittstelle und erbt dessen Implementierungen. -Maybe the easiest way for a Java programmer to understand what traits -are is to view them as interfaces which can also contain code. In -Scala, when a class inherits from a trait, it implements that trait's -interface, and inherits all the code contained in the trait. - -To see the usefulness of traits, let's look at a classical example: -ordered objects. It is often useful to be able to compare objects of a -given class among themselves, for example to sort them. In Java, -objects which are comparable implement the `Comparable` -interface. In Scala, we can do a bit better than in Java by defining -our equivalent of `Comparable` as a trait, which we will call +Um die Nützlichkeit von Traits zu demonstrieren, werden wir ein klassisches Beispiel implementieren: +Objekte mit einer natürlichen Ordnung oder Rangfolge. Es ist häufig hilfreich, Instanzen einer +Klasse untereinander vergleichen zu können, um sie beispielsweise sortieren zu können. In Java +müssen solche Objekte die Schnittstelle `Comparable` implementieren. In Scala kann dies mit einer +äquivalenten, aber besseren Variante von `Comparable` als Trait bewerkstelligt werden, genannt `Ord`. -When comparing objects, six different predicates can be useful: -smaller, smaller or equal, equal, not equal, greater or equal, and -greater. However, defining all of them is fastidious, especially since -four out of these six can be expressed using the remaining two. That -is, given the equal and smaller predicates (for example), one can -express the other ones. In Scala, all these observations can be -nicely captured by the following trait declaration: +Wenn Objekte verglichen werden, sind sechs verschiedene Aussagen sinnvoll: kleiner, kleiner gleich, +gleich, ungleich, größer, und größer gleich. Allerdings ist es umständlich, immer alle sechs +Methoden dafür zu implementieren, vor allem in Anbetracht der Tatsache, dass vier dieser sechs durch +die verbliebenen zwei ausgedrückt werden können. Sind beispielsweise die Aussagen für gleich und +kleiner gegeben, kann man die anderen damit ausdrücken. In Scala können diese Beobachtungen mit +dem folgenden Trait zusammengefasst werden: trait Ord { def < (that: Any): Boolean @@ -551,106 +494,91 @@ nicely captured by the following trait declaration: def >=(that: Any): Boolean = !(this < that) } -This definition both creates a new type called `Ord`, which -plays the same role as Java's `Comparable` interface, and -default implementations of three predicates in terms of a fourth, -abstract one. The predicates for equality and inequality do not appear -here since they are by default present in all objects. - -The type `Any` which is used above is the type which is a -super-type of all other types in Scala. It can be seen as a more -general version of Java's `Object` type, since it is also a -super-type of basic types like `Int`, `Float`, etc. - -To make objects of a class comparable, it is therefore sufficient to -define the predicates which test equality and inferiority, and mix in -the `Ord` class above. As an example, let's define a -`Date` class representing dates in the Gregorian calendar. Such -dates are composed of a day, a month and a year, which we will all -represent as integers. We therefore start the definition of the -`Date` class as follows: +Diese Definition erzeugt sowohl einen neuen Typ namens `Ord`, welcher dieselbe Rolle wie Javas +Schnittstelle `Comparable` darstellt, und drei vorgegebenen Funktionen, die auf einer vierten, +abstrakten basieren. Die Funktionen für Gleichheit und Ungleichheit erscheinen hier nicht, da sie +bereits in allen Objekten von Scala vorhanden sind. + +Der Typ `Any`, welcher oben verwendet wurde, stellt den Ober-Typ aller Typen in Scala dar. Er kann +als noch allgemeinere Version von Javas `Object` angesehen werden, da er außerdem Ober-Typ der +Basis-Typen wie `Int` und `Float` ist. + +Um Objekte einer Klasse vergleichen zu können, ist es also hinreichend, Gleichheit und die +kleiner-gleich-Beziehung zu implementieren, und dieses Verhalten gewissermaßen mit der eigentlichen +Klasse zu vermengen (mix in). Als Beispiel soll eine Klasse für Datumsangaben dienen, die Daten +eines gregorianischen Kalenders repräsentiert. Solche Daten bestehen aus Tag, Monat und Jahr, welche +durch Ganzzahlen dargestellt werden: class Date(y: Int, m: Int, d: Int) extends Ord { - def year = y + def year = y def month = m - def day = d - override def toString(): String = year + "-" + month + "-" + day + def day = d + override def toString = year + "-" + month + "-" + day + +Der wichtige Teil dieser Definition ist die `extends Ord` Deklaration, welche dem Namen der Klasse +und den Parametern folgt. Er sagt aus, dass `Date` vom Trait `Ord` erbt. + +Nun folgt eine Re-Implementierung der Methode `equals`, die von `Object` geerbt wird, so dass die +Daten korrekt nach ihren Feldern verglichen werden. Die vorgegebene Implementierung von `equals` ist +dafür nicht nützlich, da in Java Objekte physisch, also nach deren Adresse im Speicher verglichen +werden. Daher verwenden wir folgende Definition: + + override def equals(that: Any): Boolean = + that.isInstanceOf[Date] && { + val o = that.asInstanceOf[Date] + o.day == day && o.month == month && o.year == year + } -The important part here is the `extends Ord` declaration which -follows the class name and parameters. It declares that the -`Date` class inherits from the `Ord` trait. +Diese Methode verwendet die vordefinierte Methoden `isInstanceOf` und `asInstanceOf`. Erstere +entspricht Javas `instanceof`-Operator und gibt `true` zurück, genau wenn das zu testende Objekt +eine Instanz des angegebenen Typs ist. Letztere entspricht Javas Operator für Typ-Umwandlungen (cast +operator): ist das Objekt eine Instanz des angegebenen Typs, kann es als solcher angesehen und +gehandhabt werden, ansonsten wird eine `ClassCastException` ausgelöst. -Then, we redefine the `equals` method, inherited from -`Object`, so that it correctly compares dates by comparing their -individual fields. The default implementation of `equals` is not -usable, because as in Java it compares objects physically. We arrive -at the following definition: +Schließlich kann die letzte Methode definiert werden, die für `Ord` notwendig ist und die +kleiner-als-Beziehung implementiert. Diese nutzt eine andere, vordefinierte Methode, namens `error`, +des Paketes `sys`, welche eine `RuntimeException` mit der angegebenen Nachricht auslöst. + + def <(that: Any): Boolean = { + if (!that.isInstanceOf[Date]) + sys.error("cannot compare " + that + " and a Date") - override def equals(that: Any): Boolean = - that.isInstanceOf[Date] && { val o = that.asInstanceOf[Date] - o.day == day && o.month == month && o.year == year + (year < o.year) || + (year == o.year && (month < o.month || + (month == o.month && day < o.day))) } - -This method makes use of the predefined methods `isInstanceOf` -and `asInstanceOf`. The first one, `isInstanceOf`, -corresponds to Java's `instanceof` operator, and returns true -if and only if the object on which it is applied is an instance of the -given type. The second one, `asInstanceOf`, corresponds to -Java's cast operator: if the object is an instance of the given type, -it is viewed as such, otherwise a `ClassCastException` is -thrown. - -Finally, the last method to define is the predicate which tests for -inferiority, as follows. It makes use of another predefined method, -`error`, which throws an exception with the given error message. - - def <(that: Any): Boolean = { - if (!that.isInstanceOf[Date]) - 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 can be seen either as dates or as comparable objects. -Moreover, they all define the six comparison predicates mentioned -above: `equals` and `<` because they appear directly in -the definition of the `Date` class, and the others because they -are inherited from the `Ord` trait. - -Traits are useful in other situations than the one shown here, of -course, but discussing their applications in length is outside the -scope of this document. - -## Genericity - -The last characteristic of Scala we will explore in this tutorial is -genericity. Java programmers should be well aware of the problems -posed by the lack of genericity in their language, a shortcoming which -is addressed in Java 1.5. - -Genericity is the ability to write code parametrized by types. For -example, a programmer writing a library for linked lists faces the -problem of deciding which type to give to the elements of the list. -Since this list is meant to be used in many different contexts, it is -not possible to decide that the type of the elements has to be, say, -`Int`. This would be completely arbitrary and overly -restrictive. - -Java programmers resort to using `Object`, which is the -super-type of all objects. This solution is however far from being -ideal, since it doesn't work for basic types (`int`, -`long`, `float`, etc.) and it implies that a lot of -dynamic type casts have to be inserted by the programmer. - -Scala makes it possible to define generic classes (and methods) to -solve this problem. Let us examine this with an example of the -simplest container class possible: a reference, which can either be -empty or point to an object of some type. +Diese Methode vervollständigt die Definition der `Date`-Klasse. Instanzen dieser Klasse stellen +sowohl Daten als auch vergleichbare Objekte dar. Vielmehr implementiert diese Klasse alle sechs +Methoden, die für das Vergleichen von Objekten notwendig sind: `equals` und `<`, die direkt in der +Definition von `Date` vorkommen, sowie die anderen, in dem Trait `Ord` definierten Methoden. + +Traits sind nützlich in Situationen wie der obigen, deren vollen Funktionsumfang hier zu zeigen, +würde allerdings den Rahmen dieses Dokumentes sprengen. + +## Generische Programmierung + +Eine weitere Charakteristik Scalas, die in dieser Anleitung vorgestellt wird, behandelt das Konzept +der generischen Programmierung. Java-Programmierer, die die Sprache noch vor der Version 1.5 kennen, +sollten mit den Problemen vertraut sein, die auftreten, wenn generische Programmierung nicht +unterstützt wird. + +Generische Programmierung bedeutet, Quellcode nach Typen zu parametrisieren. Beispielsweise stellt +sich die Frage für einen Programmierer bei der Implementierung einer Bibliothek für verkettete +Listen, welcher Typ für die Elemente verwendet werden soll. Da diese Liste in verschiedenen +Zusammenhängen verwendet werden soll, ist es nicht möglich, einen spezifischen Typ, wie `Int`, zu +verwenden. Diese willkürliche Wahl wäre sehr einschränkend. + +Aufgrund dieser Probleme griff man in Java vor der Einführung der generischen Programmierung zu dem +Mittel, `Object`, den Ober-Typ aller Typen, als Element-Typ zu verwenden. Diese Lösung ist +allerdings auch weit entfernt von Eleganz, da sie sowohl ungeeignet für die Basis-Typen, wie `int` +oder `float`, ist als auch viele Typ-Umwandlungen für den nutzenden Programmierer bedeutet. + +Scala ermöglicht es, generische Klassen und Methoden zu definieren, um diesen Problemen aus dem Weg +zu gehen. Für die Demonstration soll ein einfacher, generischer Container als Referenz-Typ dienen, +der leer sein kann, oder auf ein Objekt des generischen Typs zeigt: class Reference[T] { private var contents: T = _ @@ -658,22 +586,17 @@ empty or point to an object of some type. def get: T = contents } -The class `Reference` is parametrized by a type, called `T`, -which is the type of its element. This type is used in the body of the -class as the type of the `contents` variable, the argument of -the `set` method, and the return type of the `get` method. +Die Klasse `Reference` ist anhand des Types `T` parametrisiert, der den Element-Typ repräsentiert. +Dieser Typ wird im Körper der Klasse genutzt, wie bei dem Feld `contents`. Dessen Argument wird +durch die Methode `get` abgefragt und mit der Methode `set` verändert. -The above code sample introduces variables in Scala, which should not -require further explanations. It is however interesting to see that -the initial value given to that variable is `_`, which represents -a default value. This default value is 0 for numeric types, -`false` for the `Boolean` type, `()` for the `Unit` -type and `null` for all object types. +Der obige Quellcode führt Variablen in Scala ein, welche keiner weiteren Erklärung erfordern +sollten. Schon interessanter ist der initiale Wert dieser Variablen, der mit `_` gekennzeichnet +wurde. Dieser Standardwert ist für numerische Typen `0`, `false` für Wahrheitswerte, `()` für den +Typ `Unit` und `null` für alle anderen Typen. -To use this `Reference` class, one needs to specify which type to use -for the type parameter `T`, that is the type of the element -contained by the cell. For example, to create and use a cell holding -an integer, one could write the following: +Um diese Referenz-Klasse zu verwenden, muss der generische Typ bei der Erzeugung einer Instanz +angegeben werden. Für einen Ganzzahl-Container soll folgendes Beispiel dienen: object IntegerReference { def main(args: Array[String]) { @@ -683,15 +606,15 @@ an integer, one could write the following: } } -As can be seen in that example, it is not necessary to cast the value -returned by the `get` method before using it as an integer. It -is also not possible to store anything but an integer in that -particular cell, since it was declared as holding an integer. +Wie in dem Beispiel zu sehen ist, muss der Wert, der von der Methode `get` zurückgegeben wird, nicht +umgewandelt werden, wenn er als Ganzzahl verwendet werden soll. Es wäre außerdem nicht möglich, ein +Wert, der keine Ganzzahl ist, in einem solchen Container zu speichern, da er speziell und +ausschließlich für Ganzzahlen erzeugt worden ist. + +## Zusammenfassung -## Conclusion +Dieses Dokument hat einen kurzen Überblick über die Sprache Scala gegeben und dazu einige einfache +Beispiele verwendet. Interessierte Leser können beispielsweise mit dem Dokument *Scala by Example* +fortfahren, welches fortgeschrittenere Beispiele enthält, und die *Scala Language Specification* +konsultieren, sofern nötig. -This document gave a quick overview of the Scala language and -presented some basic examples. The interested reader can go on, for example, by -reading the document *Scala By Example*, which -contains much more advanced examples, and consult the *Scala - Language Specification* when needed. From 46ec8ea36384de7829d91d01aabfe9b69245c393 Mon Sep 17 00:00:00 2001 From: Christian Krause Date: Mon, 14 May 2012 13:58:53 +0200 Subject: [PATCH 6/6] improved translation --- de/tutorials/scala-for-java-programmers.md | 332 +++++++++++---------- 1 file changed, 173 insertions(+), 159 deletions(-) diff --git a/de/tutorials/scala-for-java-programmers.md b/de/tutorials/scala-for-java-programmers.md index 9e2a508b53..3c830e5d75 100644 --- a/de/tutorials/scala-for-java-programmers.md +++ b/de/tutorials/scala-for-java-programmers.md @@ -1,6 +1,6 @@ --- layout: overview -title: Eine Scala Anleitung für Java Programmierer +title: Ein Scala Tutorial für Java Programmierer overview: scala-for-java-programmers disqus: true @@ -13,15 +13,15 @@ Deutsche Übersetzung von Christian Krause. ## Einleitung -Diese Anleitung soll als kurze Vorstellung der Programmiersprache Scala und deren Compiler dienen. -Sie ist für fortgeschrittene Programmierer gedacht, die sich einen Überblick über Scala verschaffen -wollen und wie man mit ihr arbeitet. Grundkenntnisse in Objekt-orientierter Programmierung, -insbesondere Java, werden vorausgesetzt. +Dieses Tutorial dient einer kurzen Vorstellung der Programmiersprache Scala und deren Compiler. Sie +ist für fortgeschrittene Programmierer gedacht, die sich einen Überblick darüber verschaffen wollen, +wie man mit Scala arbeitet. Grundkenntnisse in Objekt-orientierter Programmierung, insbesondere +Java, werden vorausgesetzt. ## Das erste Beispiel Als erstes folgt eine Implementierung des wohlbekannten *Hallo, Welt!*-Programmes. Obwohl es sehr -einfach ist, eignet es sich sehr gut, Scala's Funktionsweise zu demonstrieren, ohne dass man viel +einfach ist, eignet es sich sehr gut, Scalas Funktionsweise zu demonstrieren, ohne dass man viel über die Sprache wissen muss. object HalloWelt { @@ -34,62 +34,60 @@ Die Struktur des Programmes sollte Java Anwendern bekannt vorkommen: es besteht namens `main`, welche die Kommandozeilenparameter als Feld (Array) von Zeichenketten (String) übergeben bekommt. Der Körper dieser Methode besteht aus einem einzelnen Aufruf der vordefinierten Methode `println`, die die freundliche Begrüßung als Parameter übergeben bekommt. Weiterhin hat die -Methode `main` keinen Rückgabewert, sie ist also eine Prozedur. Daher ist es auch nicht notwendig, -einen Rückgabetyp zu deklarieren. +`main`-Methode keinen Rückgabewert - sie ist also eine Prozedur. Daher ist es auch nicht notwendig, +einen Rückgabetyp zu spezifizieren. -Was Java Programmierern allerdings weniger bekannt sein sollte, ist die Deklaration `object`, welche -die Methode `main` enthält. Eine solche Deklaration stellt dar, was gemeinhin als *Singleton Objekt* -(Einzelstück) bekannt ist, also eine Klasse mit nur einer Instanz. Im Beispiel oben werden also mit +Was Java-Programmierern allerdings weniger bekannt sein sollte, ist die Deklaration `object +HalloWelt`, welche die Methode `main` enthält. Eine solche Deklaration stellt dar, was gemeinhin als +*Singleton Objekt* bekannt ist: eine Klasse mit nur einer Instanz. Im Beispiel oben werden also mit dem Schlüsselwort `object` sowohl eine Klasse namens `HalloWelt` als auch die dazugehörige, -gleichnamige Instanz deklariert. Diese Instanz wird erst bei ihrer erstmaligen Verwendung erstellt. +gleichnamige Instanz definiert. Diese Instanz wird erst bei ihrer erstmaligen Verwendung erstellt. -Dem aufmerksamen Leser wird vielleicht aufgefallen sein, dass die Methode `main` nicht als `static` +Dem aufmerksamen Leser ist vielleicht aufgefallen, dass die `main`-Methode nicht als `static` deklariert wurde. Der Grund dafür ist, dass statische Mitglieder (Attribute oder Methoden) in Scala -nicht existieren. Statt statische Mitglieder zu definieren deklariert ein Scala Programmierer diese -Mitglieder in Singleton Objekten. +nicht existieren. Die Mitglieder von Singleton Objekten stellen in Scala dar, was Java und andere +Sprachen mit statischen Mitgliedern erreichen. ### Das Beispiel kompilieren Um das obige Beispiel zu kompilieren, wird `scalac`, der Scala-Compiler verwendet. `scalac` arbeitet wie die meisten anderen Compiler auch: er akzeptiert Quellcode-Dateien als Parameter, einige weitere Optionen, und übersetzt den Quellcode in Java-Bytecode. Dieser Bytecode wird in ein oder mehrere -Java-konforme Class-Dateien (mit der Endung `.class`) geschrieben. +Java-konforme Klassen-Dateien, Dateien mit der Endung `.class`, geschrieben. Schreibt man den obigen Quellcode in eine Datei namens `HalloWelt.scala`, kann man diese mit dem -folgenden Befehl kompilieren (das größer als Zeichen `>` repräsentiert die Eingabeaufforderung und +folgenden Befehl kompilieren (das größer-als-Zeichen `>` repräsentiert die Eingabeaufforderung und sollte nicht mit geschrieben werden): > scalac HalloWelt.scala -Damit werden ein paar Class-Dateien in das aktuelle Verzeichnis geschrieben. Eine davon heißt +Damit werden einige Klassen-Dateien in das aktuelle Verzeichnis geschrieben. Eine davon heißt `HalloWelt.class` und enthält die Klasse, die direkt mit dem Befehl `scala` ausgeführt werden kann, -was in den folgenden Abschnitten erklärt wird. +was im folgenden Abschnitt erklärt wird. ### Das Beispiel ausführen -Sobald kompiliert, kann ein Scala Programm mit dem Befehl `scala` ausgeführt werden. Die Anwendung -ist dem Befehl `java`, mit dem man Java Programme ausführt, nachempfunden und akzeptiert die selben +Sobald kompiliert, kann ein Scala-Programm mit dem Befehl `scala` ausgeführt werden. Die Anwendung +ist dem Befehl `java`, mit dem man Java-Programme ausführt, nachempfunden und akzeptiert dieselben Optionen. Das obige Beispiel kann demnach mit folgendem Befehl ausgeführt werden, was das erwartete Resultat ausgibt: > scala -classpath . HalloWelt - Hallo, Welt! ## Interaktion mit Java Eine Stärke der Sprache Scala ist, dass man mit ihr sehr leicht mit Java interagieren kann. Alle -Klassen des Paketes `java.lang` werden automatisch importiert, während andere explizit importiert -werden müssen. +Klassen des Paketes `java.lang` stehen beispielsweise automatisch zur Verfügung, während andere +explizit importiert werden müssen. Als nächstes folgt ein Beispiel, was diese Interoperabilität demonstriert. Ziel ist es, das aktuelle Datum zu erhalten und gemäß den Konventionen eines gewissen Landes zu formatieren, zum Beispiel -Frankreich (andere Regionen, wie der französisch-sprachige Teil der Schweiz verwenden dieselben -Konventionen). +Frankreich. Javas Klassen-Bibliothek enthält viele nützliche Klassen, beispielsweise `Date` und `DateFormat`. -Dank Scala's Fähigkeit, nahtlos mit Java zu interoperieren, besteht keine Notwendigkeit, äquivalente -Klassen in der Scala Klassen-Bibliothek zu implementieren--man kann einfach die entsprechenden +Dank Scala Fähigkeit, nahtlos mit Java zu interoperieren, besteht keine Notwendigkeit, äquivalente +Klassen in der Scala Klassen-Bibliothek zu implementieren - man kann einfach die entsprechenden Klassen der Java-Pakete importieren: import java.util.{Date, Locale} @@ -104,20 +102,20 @@ Klassen der Java-Pakete importieren: } } -Scala's Import-Anweisung ähnelt sehr der aus Java, obwohl sie viel mächtiger ist. Mehrere Klassen -des gleichen Paketes können gleichzeitig importiert werden, indem sie, wie in der ersten Zeile, in +Scala Import-Anweisung ähnelt sehr der von Java, obwohl sie viel mächtiger ist. Mehrere Klassen des +gleichen Paketes können gleichzeitig importiert werden, indem sie, wie in der ersten Zeile, in geschweifte Klammern geschrieben werden. Ein weiterer Unterschied ist, dass, wenn man alle Mitglieder eines Paketes importieren will, einen Unterstrich (`_`) anstelle des Asterisk (`*`) -verwendet. Das liegt daran, dass der Asterisk ein gültiger Bezeichner in Scala ist, beispielsweise -als Name für Methoden, wie später gezeigt wird. Die Import-Anweisung der dritten Zeile importiert -demnach alle Mitglieder der Klasse `DateFormat`, inklusive der statischen Methode `getDateInstance` -und des statischen Feldes `LONG`. +verwendet. Der Grund dafür ist, dass der Asterisk ein gültiger Bezeichner in Scala ist, +beispielsweise als Name für Methoden, wie später gezeigt wird. Die Import-Anweisung der dritten +Zeile importiert demnach alle Mitglieder der Klasse `DateFormat`, inklusive der statischen Methode +`getDateInstance` und des statischen Feldes `LONG`. -Innerhalb der Methode `main` wird zuerst eine Instanz der Java-Klasse `Date` erzeugt, welche, per -Default, das aktuelle Datum enthält. Als nächstes wird mithilfe der statischen Methode +Innerhalb der `main`-Methode wird zuerst eine Instanz der Java-Klasse `Date` erzeugt, welche +standardmäßig das aktuelle Datum enthält. Als nächstes wird mithilfe der statischen Methode `getDateInstance` eine Instanz der Klasse `DateFormat` erstellt. Schließlich wird das aktuelle Datum gemäß der Regeln der lokalisierten `DateFormat`-Instanz formatiert ausgegeben. Außerdem -veranschaulicht die letzte Zeile eine interessante Fähigkeit Scala's Syntax: Methoden, die nur einen +veranschaulicht die letzte Zeile eine interessante Fähigkeit Scalas Syntax: Methoden, die nur einen Parameter haben, können in der Infix-Syntax notiert werden. Dies bedeutet, dass der Ausdruck df format now @@ -126,10 +124,11 @@ eine andere, weniger verbose Variante des folgenden Ausdruckes ist: df.format(now) -Es scheint nur ein nebensächliches, syntaktisches Detail zu sein, hat jedoch bedeutende -Konsequenzen, was im nächsten Abschnitt gezeigt wird. Um diesen Abschnitt abzuschließen, soll -bemerkt werden, dass es außerdem direkt in Scala möglich ist, von Java-Klassen zu erben -Java-Schnittstellen zu implementieren. +Dies scheint nur ein nebensächlicher, syntaktischer Zucker zu sein, hat jedoch bedeutende +Konsequenzen, wie im folgenden Abschnitt gezeigt wird. + +Um diesen Abschnitt abzuschließen, soll bemerkt sein, dass es außerdem direkt in Scala möglich ist, +von Java-Klassen zu erben sowie Java-Schnittstellen zu implementieren. ## Alles ist ein Objekt @@ -141,7 +140,7 @@ Werte zu behandeln. ### Zahlen sind Objekte Zahlen sind Objekte und haben daher Methoden. Tatsächlich besteht ein arithmetischer Ausdruck wie -der folgende: +der folgende 1 + 2 * 3 / x @@ -152,25 +151,25 @@ Abschnitt gezeigt wurde: Dies bedeutet außerdem, dass `+`, `*`, etc. in Scala gültige Bezeichner sind. -Die Zahlen umschließenden Klammern der zweiten Variante sind notwendig, weil Scala's lexikalischer +Die Zahlen umschließenden Klammern der zweiten Variante sind notwendig, weil Scalas lexikalischer Scanner eine Regel zur längsten Übereinstimmung der Token verwendet. Daher würde der folgende Ausdruck: 1.+(2) -in die Token `1.`, `+`, und `2` zerlegt werden. Der Grund für diese Zerlegung ist, dass `1.` ein -längere, gültige Übereinstimmung ist, als `1`. Daher wird das Token `1.` als das Literal `1.0` -interpretiert, also als Gleitkomma- statt als Ganzzahl. Den Ausdruck als +in die Token `1.`, `+`, und `2` zerlegt werden. Der Grund für diese Zerlegung ist, dass `1.` eine +längere, gültige Übereinstimmung ist, als `1`. Daher würde das Token `1.` als das Literal `1.0` +interpretiert, also als Gleitkommazahl anstatt als Ganzzahl. Den Ausdruck als (1).+(2) -zu schreiben, verhindert also, dass `1` als Gleitkommazahl interpretiert wird. +zu schreiben, verhindert also, dass `1.` als Gleitkommazahl interpretiert wird. ### Funktionen sind Objekte -Vermutlich überraschender für Java Programmierer ist, dass auch Funktionen in Scala Objekte sind. +Vermutlich überraschender für Java-Programmierer ist, dass auch Funktionen in Scala Objekte sind. Daher ist es auch möglich, Funktionen als Parameter zu übergeben, als Werte zu speichern, und von -anderen Funktionen zurück geben zu lassen. Diese Fähigkeit, Funktionen wie Werte zu behandeln, ist +anderen Funktionen zurückgeben zu lassen. Diese Fähigkeit, Funktionen wie Werte zu behandeln, ist einer der Grundsteine eines sehr interessanten Programmier-Paradigmas, der *funktionalen Programmierung*. @@ -178,63 +177,72 @@ Ein sehr einfaches Beispiel, warum es nützlich sein kann, Funktionen wie Werte eine Timer-Funktion, deren Ziel es ist, eine gewisse Aktion pro Sekunde durchzuführen. Wie übergibt man die durchzuführende Aktion? Offensichtlich als Funktion. Diese einfache Art der Übergabe einer Funktion sollte den meisten Programmieren bekannt vorkommen: dieses Prinzip wird häufig bei -Benutzerschnittstellen für Rückruf-Funktionen (call-back) verwendet, die ausgeführt werden, wenn ein +Schnittstellen für Rückruf-Funktionen (call-back) verwendet, die ausgeführt werden, wenn ein bestimmtes Ereignis eintritt. Im folgenden Programm akzeptiert die Timer-Funktion `oncePerSecond` eine Rückruf-Funktion als -Parameter. Der Typ wird `() => Unit` geschrieben und ist der Typ aller Funktionen, die keine -Parameter haben und nichts zurück geben (der Typ `Unit` ist das Äquivalent zu `void` in C/C++ und -Java). Die Methode `main` des Programmes ruft die Timer-Funktion mit der Rückruf-Funktion auf, die -einen Satz ausgibt. In anderen Worten: das Programm gibt endlos den Satz "Die Zeit vergeht wie im -Flug." einmal pro Sekunde aus. +Parameter. Deren Typ wird `() => Unit` geschrieben und ist der Typ aller Funktionen, die keine +Parameter haben und nichts zurück geben (der Typ `Unit` ist das Äquivalent zu `void`). Die +`main`-Methode des Programmes ruft die Timer-Funktion mit der Rückruf-Funktion auf, die einen Satz +ausgibt. In anderen Worten: das Programm gibt endlos den Satz "Die Zeit vergeht wie im Flug." +einmal pro Sekunde aus. object Timer { def oncePerSecond(callback: () => Unit) { - while (true) { callback(); Thread sleep 1000 } + while (true) { + callback() + Thread sleep 1000 + } } + def timeFlies() { println("Die Zeit vergeht wie im Flug.") } + def main(args: Array[String]) { oncePerSecond(timeFlies) } } -Zu bemerken ist, dass um die Zeichenkette auszugeben, die in Scala vordefinierte Methode `println` -statt der äquivalenten Methode in `System.out` verwendet wird. +Weiterhin ist zu bemerken, dass, um die Zeichenkette auszugeben, die in Scala vordefinierte Methode +`println` statt der äquivalenten Methode in `System.out` verwendet wird. #### Anonyme Funktionen -Während das obige Programm schon leicht zu verstehen ist, kann es noch modifiziert werden. Als -erstes ist zu bemerken, dass die Funktion `timeFlies` nur definiert wurde, um der Funktion -`oncePerSecond` als Parameter übergeben zu werden. Dieser nur einmal verwendeten Funktion einen -Namen zu geben, scheint unnötig und es wäre angenehmer, sie direkt mit der Übergabe zu erstellen. -Das ist in Scala mit *anonymen Funktionen* möglich, die genau das darstellen: eine Funktion ohne -Namen. Die überarbeitete Version des obigen Timer-Programmes verwendet eine anonyme Funktion anstatt -der Funktion `timeFlies`: +Während das obige Programm schon leicht zu verstehen ist, kann es noch verbessert werden. Als erstes +sei zu bemerken, dass die Funktion `timeFlies` nur definiert wurde, um der Funktion `oncePerSecond` +als Parameter übergeben zu werden. Dieser nur einmal verwendeten Funktion einen Namen zu geben, +scheint unnötig und es wäre angenehmer, sie direkt mit der Übergabe zu erstellen. Dies ist in Scala +mit *anonymen Funktionen* möglich, die eine Funktion ohne Namen darstellen. Die überarbeitete +Variante des obigen Timer-Programmes verwendet eine anonyme Funktion anstatt der Funktion +`timeFlies`: object TimerAnonymous { def oncePerSecond(callback: () => Unit) { - while (true) { callback(); Thread sleep 1000 } + while (true) { + callback() + Thread sleep 1000 + } } + def main(args: Array[String]) { oncePerSecond(() => println("Die Zeit vergeht wie im Flug.")) } } -Die anonyme Funktion erkennt man an dem Rechtspfeil `=>`, der die Parameter vom Körper der Funktion -trennt. In diesem Beispiel ist die Liste der Parameter leer, wie man an den leeren Klammern erkennen -kann. Der Körper der Funktion ist derselbe, wie bei der `timeFlies` Funktion des vorangegangenen -Beispiels. +Die anonyme Funktion erkennt man an dem Rechtspfeil `=>`, der die Parameter der Funktion von deren +Körper trennt. In diesem Beispiel ist die Liste der Parameter leer, wie man an den leeren Klammern +erkennen kann. Der Körper der Funktion ist derselbe, wie bei der `timeFlies` Funktion des +vorangegangenen Beispiels. ## Klassen -Wie oben zu sehen war, ist Scala eine Objekt-orientierte Sprache, und als solche enthält sie das -Konzept von Klassen (der Vollständigkeit halber soll bemerkt sein, dass nicht alle +Wie weiter oben zu sehen war, ist Scala eine pur Objekt-orientierte Sprache, und als solche enthält +sie das Konzept von Klassen (der Vollständigkeit halber soll bemerkt sein, dass nicht alle Objekt-orientierte Sprachen das Konzept von Klassen unterstützen, aber Scala ist keine von denen). Klassen in Scala werden mit einer ähnlichen Syntax wie Java deklariert. Ein wichtiger Unterschied -ist, dass Scala's Klassen Argumente haben. Dies wird mit der folgenden Definition von komplexen -Zahlen veranschaulicht: +ist jedoch, dass Scalas Klassen Argumente haben. Dies soll mit der folgenden Definition von +komplexen Zahlen veranschaulicht werden: class Complex(real: Double, imaginary: Double) { def re() = real @@ -242,26 +250,26 @@ Zahlen veranschaulicht: } Diese Klasse akzeptiert zwei Argumente, den realen und den imaginären Teil der komplexen Zahl. Sie -müssen beim kreieren einer Instanz der Klasse übergeben werden: +müssen beim Erzeugen einer Instanz der Klasse übergeben werden: val c = new Complex(1.5, 2.3) Weiterhin enthält die Klasse zwei Methoden, `re` und `im`, welche als Zugriffsfunktionen (Getter) -dienen. Außerdem soll bemerkt sein, dass der Rückgabewert dieser Methoden nicht explizit deklariert -ist. Der Compiler schlussfolgert automatisch, indem er ihn aus dem rechten Teil der Methoden +dienen. Außerdem soll bemerkt sein, dass der Rückgabe-Typ dieser Methoden nicht explizit deklariert +ist. Der Compiler schlussfolgert ihn automatisch, indem er ihn aus dem rechten Teil der Methoden ableitet, dass der Rückgabewert vom Typ `Double` ist. -Der Compiler ist nicht immer fähig, auf den Rückgabewert zu schließen, und es gibt leider keine -einfache Regel, um zu wissen, dies vorauszusagen. In der Praxis ist das üblicherweise kein Problem, -da der Compiler sich beschwert, wenn es ihm nicht möglich ist. Als einfache Regel sollten -Scala-Anfänger versuchen, Typ-Deklarationen, die leicht vom Kontext abzuleiten sind, wegzulassen, um -zu sehen, ob der Compiler zustimmt. Nach einer gewissen Zeit, bekommt man ein Gefühl dafür, wann man -auf diese Deklarationen verzichten kann und wann man sie explizit angeben sollte. +Der Compiler ist nicht immer fähig, auf den Rückgabe-Typ zu schließen, und es gibt leider keine +einfache Regel, vorauszusagen, ob er dazu fähig ist oder nicht. In der Praxis stellt das +üblicherweise kein Problem dar, da der Compiler sich beschwert, wenn es ihm nicht möglich ist. +Scala-Anfänger sollten versuchen, Typ-Deklarationen, die leicht vom Kontext abzuleiten sind, +wegzulassen, um zu sehen, ob der Compiler zustimmt. Nach einer gewissen Zeit, bekommt man ein Gefühl +dafür, wann man auf diese Deklarationen verzichten kann und wann man sie explizit angeben sollte. ### Methoden ohne Argumente -Ein Problem der obigen Methoden `re` und `im` ist, dass man, um sie zu verwenden, ein Paar leerer -Klammern hinter ihren Namen anhängen muss: +Ein Problem der obigen Methoden `re` und `im` ist, dass man, um sie zu verwenden, ein leeres +Klammerpaar hinter ihren Namen anhängen muss: object ComplexNumbers { def main(args: Array[String]) { @@ -270,8 +278,8 @@ Klammern hinter ihren Namen anhängen muss: } } -Besser wäre jedoch, wenn man den realen und imaginären Teil so abrufen könnte, als wären sie Felder, -also ohne das leere Klammerpaar. Mit Scala ist das möglich, indem Methoden *ohne Argumente* +Besser wäre es jedoch, wenn man den realen und imaginären Teil so abrufen könnte, als wären sie +Felder, also ohne das leere Klammerpaar. Mit Scala ist dies möglich, indem Methoden *ohne Argumente* definiert werden. Solche Methoden haben keine Klammern nach ihrem Namen, weder bei ihrer Definition noch bei ihrer Verwendung. Die Klasse für komplexe Zahlen kann demnach folgendermaßen umgeschrieben werden: @@ -281,33 +289,34 @@ werden: def im = imaginary } -### Vererbung und Überschreiben +### Vererbung und Überschreibung -Alle Klassen in Scala erben von einer Oberklasse. Wird keine Oberklasse spezifiziert, wie bei der +Alle Klassen in Scala erben von einer Oberklasse. Wird keine Oberklasse angegeben, wie bei der Klasse `Complex` des vorhergehenden Abschnittes, wird implizit `scala.AnyRef` verwendet. -Es ist möglich, von einer Oberklasse vererbte Methoden zu überschreiben. Dabei muss jedoch explizit -das Schlüsselwort `override` angegeben werden, um versehentliche Überschreibungen zu vermeiden. Als -Beispiel soll eine Erweiterung der Klasse `Complex` dienen, die die Methode `toString` neu -definiert: +Außerdem ist es möglich, von einer Oberklasse vererbte Methoden zu überschreiben. Dabei muss jedoch +explizit das Schlüsselwort `override` angegeben werden, um versehentliche Überschreibungen zu +vermeiden. Als Beispiel soll eine Erweiterung der Klasse `Complex` dienen, die die Methode +`toString` neu definiert: class Complex(real: Double, imaginary: Double) { def re = real def im = imaginary + override def toString() = "" + re + (if (im < 0) "" else "+") + im + "i" } -## Container-Klassen und Musterabgleich +## Container-Klassen und Musterabgleiche -Eine Datenstruktur, die häufig in Programmen vorkommt ist der Baum, beispielsweise repräsentieren +Eine Datenstruktur, die häufig in Programmen vorkommt, ist der Baum. Beispielsweise repräsentieren Interpreter und Compiler Programme intern häufig als Bäume, XML-Dokumente sind Bäume und einige Container basieren auf Bäumen, wie Rot-Schwarz-Bäume. Als nächstes wird anhand eines kleinen Programmes für Berechnungen gezeigt, wie solche Bäume in Scala repräsentiert und manipuliert werden können. Das Ziel dieses Programmes ist, einfache arithmetische Ausdrücke zu manipulieren, die aus Summen, Ganzzahlen und Variablen bestehen. -Beispiele solcher Ausdrücke sind: `1 + 2` und `(x+x) + (7+y)`. +Beispiele solcher Ausdrücke sind: `1+2` und `(x+x)+(7+y)`. Dafür muss zuerst eine Repräsentation für die Ausdrücke gewählt werden. Die natürlichste ist ein Baum, dessen Knoten Operationen (Additionen) und dessen Blätter Werte (Konstanten und Variablen) @@ -317,7 +326,7 @@ In Java würde man solche Bäume am ehesten mithilfe einer abstrakten Oberklasse konkreten Implementierungen für Knoten und Blätter repräsentieren. In einer funktionalen Sprache würde man algebraische Datentypen mit dem gleichen Ziel verwenden. Scala unterstützt das Konzept einer Container-Klasse (case class), die einen gewissen Mittelweg dazwischen darstellen. Der -folgenden Quelltext veranschaulicht deren Anwendung: +folgenden Quellcode veranschaulicht deren Anwendung: abstract class Tree case class Sum(l: Tree, r: Tree) extends Tree @@ -327,16 +336,16 @@ folgenden Quelltext veranschaulicht deren Anwendung: Die Tatsache, dass die Klassen `Sum`, `Var` und `Const` als Container-Klassen deklariert sind, bedeutet, dass sie sich in einigen Gesichtspunkten von normalen Klassen unterscheiden: -- das Schlüsselwort `new` ist nicht mehr notwendig, um Instanzen dieser Klassen zu erzeugen (man - kann also `Const(5)` statt `new Const(5)` schreiben) -- Zugriffsfunktionen werden automatisch anhand der Parameter des Konstruktors erstellt (man kann - den Wert `v` einer Instanz `c` der Klasse `Const` erhalten, indem man `c.v` schreibt) -- der Compiler fügt Container-Klassen automatisch Implementierungen der Methoden `equals` und - `hashCode` hinzu, die auf der *Struktur* der Klassen basieren, nicht deren Identität -- außerdem wird eine `toString` Methode bereitgestellt, die einen Wert in Form der Quelle darstellt - (der String-Wert des Baum-Ausdruckes `x+1` ist `Sum(Var(x),Const(1))`) -- Instanzen dieser Klassen können mithilfe von Musterabgleich zerlegt werden, wie weiter unten zu - sehen ist +- das Schlüsselwort `new` ist nicht mehr notwendig, um Instanzen dieser Klassen zu erzeugen (man + kann also `Const(5)` anstelle von `new Const(5)` schreiben) +- Zugriffsfunktionen werden automatisch anhand der Parameter des Konstruktors erstellt (man kann + den Wert `v` einer Instanz `c` der Klasse `Const` erhalten, indem man `c.v` schreibt) +- der Compiler fügt Container-Klassen automatisch Implementierungen der Methoden `equals` und + `hashCode` hinzu, die auf der *Struktur* der Klassen basieren, anstelle deren Identität +- außerdem wird eine `toString`-Methode bereitgestellt, die einen Wert in Form der Quelle + darstellt (der String-Wert des Baum-Ausdruckes `x+1` ist `Sum(Var(x),Const(1))`) +- Instanzen dieser Klassen können mithilfe von Musterabgleichen zerlegt werden, wie weiter unten + zu sehen ist Da jetzt bekannt ist, wie die Datenstruktur der arithmetischen Ausdrücke repräsentiert wird, können jetzt Operationen definiert werden, um diese zu manipulieren. Der Beginn dessen soll eine Funktion @@ -356,8 +365,8 @@ Diese Schreibweise definiert eine Funktion, welche bei dem String `"x"` als Argu Vor dem Schreiben der Funktionen zum Auswerten ist es sinnvoll, für die Umgebungen einen eigenen Typ zu definieren. Man könnte zwar immer `String => Int` verwenden, es wäre jedoch besser einen -dedizierten Namen dafür zu verwenden, der das Programmieren damit einfacher macht. Dies wird in -Scala mit der folgenden Schreibweise erreicht: +dedizierten Namen dafür zu verwenden, der das Programmieren damit einfacher macht und die Lesbarkeit +erhöht. Dies wird in Scala mit der folgenden Schreibweise erreicht: type Environment = String => Int @@ -365,9 +374,9 @@ Von hier an wird `Environment` als Alias für den Typ von Funktionen von `String verwendet. Nun ist alles für die Definition der Funktion zur Auswertung vorbereitet. Konzeptionell ist die -Definition sehr einfach: der Wert der Summe zweier Ausdrücke ist die Summe der Werte der -einzelnen Ausdrücke, der Wert einer Variable wird direkt der Umgebung entnommen und der Wert einer -Konstante ist die Konstante selbst. Dies in Scala auszudrücken ist nicht viel komplizierter: +Definition sehr einfach: der Wert der Summe zweier Ausdrücke ist die Summe der Werte der einzelnen +Ausdrücke, der Wert einer Variablen wird direkt der Umgebung entnommen und der Wert einer Konstante +ist die Konstante selbst. Dies in Scala auszudrücken, ist nicht viel schwieriger: def eval(t: Tree, env: Environment): Int = t match { case Sum(l, r) => eval(l, env) + eval(r, env) @@ -375,8 +384,8 @@ Konstante ist die Konstante selbst. Dies in Scala auszudrücken ist nicht viel k case Const(v) => v } -Diese Funktion zum Auswerten von arithmetischen Ausdrücken nutzt *Musterabgleich* (pattern -matching) anhand des Baumes `t`. Intuitiv sollte die Bedeutung der einzelnen Fälle klar sein: +Diese Funktion zum Auswerten von arithmetischen Ausdrücken nutzt einen *Musterabgleich* (pattern +matching) am Baumes `t`. Intuitiv sollte die Bedeutung der einzelnen Fälle klar sein: 1. Als erstes wird überprüft, ob `t` eine Instanz der Klasse `Sum` ist. Falls dem so ist, wird der linke Teilbaum der Variablen `l` und der rechte Teilbaum der Variablen `r` zugewiesen. Daraufhin @@ -392,23 +401,22 @@ ob es eine Instanz des Typs `Const` ist. Analog wird bei einem Erfolg wie bei de vorangegangenen Fällen verfahren. 4. Schließlich, sollten alle Überprüfungen fehlschlagen, wird eine Ausnahme ausgelöst, die -signalisiert, dass die Musterabgleich nicht erfolgreich war. Dies wird unweigerlich geschehen, +signalisiert, dass der Musterabgleich nicht erfolgreich war. Dies wird unweigerlich geschehen, sollten neue Baum-Unterklassen erstellt werden. -Die prinzipielle Idee eines Musterabgleich ist, einen Wert anhand einer Reihe von Mustern +Die prinzipielle Idee eines Musterabgleiches ist, einen Wert anhand einer Reihe von Mustern abzugleichen und, sobald ein Treffer erzielt wird, Werte zu extrahieren, mit denen darauf weitergearbeitet werden kann. Erfahrene Objekt-orientierte Programmierer werden sich fragen, warum `eval` nicht als Methode der Klasse `Tree` oder dessen Unterklassen definiert wurde. Dies wäre möglich, da Container-Klassen -Methoden definieren können wie normale Klassen auch. Die Entscheidung, einen Musterabgleich oder +Methoden definieren können, wie normale Klassen auch. Die Entscheidung, einen Musterabgleich oder Methoden zu verwenden, ist Geschmackssache, hat jedoch wichtige Auswirkungen auf die Erweiterbarkeit: - einerseits ist es mit Methoden einfach, neue Arten von Knoten als Unterklassen von `Tree` hinzuzufügen, andererseits ist die Ergänzung einer neuen Operation zur Manipulation des Baumes - mühsam, da sie die Modifikation aller Unterklassen von `Tree` notwendig macht - + mühsam, da sie die Modifikation aller Unterklassen von `Tree` erfordert - nutzt man einen Musterabgleich kehrt sich die Situation um: eine neue Art von Knoten erfordert die Modifikation aller Funktionen die einen Musterabgleich am Baum vollführen, wogegen eine neue Operation leicht hinzuzufügen ist, indem einfach eine unabhängige Funktion dafür definiert wird @@ -417,10 +425,10 @@ Einen weiteren Einblick in Musterabgleiche verschafft eine weitere Operation mit Ausdrücken: partielle Ableitungen. Dafür gelten zur Zeit folgende Regeln: 1. die Ableitung einer Summe ist die Summe der Ableitungen -2. die Ableitung einer Variablen ist eins, wenn sie die abzuleitende Variable ist, ansonsten null -3. die Ableitung einer Konstanten ist null +2. die Ableitung einer Variablen ist eins, wenn sie die abzuleitende Variable ist, ansonsten `0` +3. die Ableitung einer Konstanten ist `0` -Diese Regeln können fast wörtlich in Scala übersetzt werden: +Auch diese Regeln können fast wörtlich in Scala übersetzt werden: def derive(t: Tree, v: String): Tree = t match { case Sum(l, r) => Sum(derive(l, v), derive(r, v)) @@ -432,14 +440,14 @@ Diese Funktion führt zwei neue, mit dem Musterabgleich zusammenhängende Konzep sich auf eine Variable beziehende Fall hat eine *Sperre* (guard), einen Ausdruck, der dem Schlüsselwort `if` folgt. Diese Sperre verhindert eine Übereinstimmung, wenn der Ausdruck falsch ist. In diesem Fall wird sie genutzt, die Konstante `1` nur zurückzugeben, wenn die Variable die -abzuleitende ist. Die zweite Neuerung ist der *Platzhalter* `_`, der mit allem übereinstimmt, ohne -einen Namen dafür zu verwenden. +abzuleitende ist. Die zweite Neuerung ist der *Platzhalter* `_`, der mit allem übereinstimmt, jedoch +ohne einen Namen dafür zu verwenden. -Die volle Funktionalität von Musterabgleichen wurde mit diesen Beispielen nicht demonstriert, -doch soll dies fürs Erste genügen. Eine Vorführung der beiden Funktionen an realen Beispielen steht -immer noch aus. Zu diesem Zweck soll eine `main` Methode dienen, die den Ausdruck `(x+x)+(7+y)` als -Beispiel verwendet: zuerst wird der Wert in der Umgebung `{ x -> 5, y -> 7 }` und darauf die -beiden partiellen Ableitungen. +Die volle Funktionalität von Musterabgleichen wurde mit diesen Beispielen nicht demonstriert, doch +soll dies fürs Erste genügen. Eine Vorführung der beiden Funktionen an realen Beispielen steht immer +noch aus. Zu diesem Zweck soll eine `main`-Methode dienen, die den Ausdruck `(x+x)+(7+y)` als +Beispiel verwendet: zuerst wird der Wert in der Umgebung `{ x -> 5, y -> 7 }` berechnet und darauf +die beiden partiellen Ableitungen gebildet: def main(args: Array[String]) { val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) @@ -463,8 +471,8 @@ Führt man das Programm aus, erhält man folgende Ausgabe: Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) Beim Anblick dieser Ausgabe ist offensichtlich, dass man die Ergebnisse der Ableitungen noch -vereinfachen sollte. Eine solche Funktion zum Vereinfachen von Ausdrücken, die Musterabgleiche nutzt -ist ein interessantes, aber gar nicht so einfaches Problem, was als Übung implementiert werden kann. +vereinfachen sollte. Eine solche Funktion zum Vereinfachen von Ausdrücken, die Musterabgleiche +nutzt, ist ein interessantes, aber gar nicht so einfaches Problem, was als Übung offen steht. ## Traits @@ -476,9 +484,9 @@ erbt, implementiert sie dessen Schnittstelle und erbt dessen Implementierungen. Um die Nützlichkeit von Traits zu demonstrieren, werden wir ein klassisches Beispiel implementieren: Objekte mit einer natürlichen Ordnung oder Rangfolge. Es ist häufig hilfreich, Instanzen einer Klasse untereinander vergleichen zu können, um sie beispielsweise sortieren zu können. In Java -müssen solche Objekte die Schnittstelle `Comparable` implementieren. In Scala kann dies mit einer -äquivalenten, aber besseren Variante von `Comparable` als Trait bewerkstelligt werden, genannt -`Ord`. +müssen die Klassen solcher Objekte die Schnittstelle `Comparable` implementieren. In Scala kann dies +mit einer äquivalenten, aber besseren Variante von `Comparable` als Trait bewerkstelligt werden, die +im Folgenden `Ord` genannt wird. Wenn Objekte verglichen werden, sind sechs verschiedene Aussagen sinnvoll: kleiner, kleiner gleich, gleich, ungleich, größer, und größer gleich. Allerdings ist es umständlich, immer alle sechs @@ -495,8 +503,8 @@ dem folgenden Trait zusammengefasst werden: } Diese Definition erzeugt sowohl einen neuen Typ namens `Ord`, welcher dieselbe Rolle wie Javas -Schnittstelle `Comparable` darstellt, und drei vorgegebenen Funktionen, die auf einer vierten, -abstrakten basieren. Die Funktionen für Gleichheit und Ungleichheit erscheinen hier nicht, da sie +Schnittstelle `Comparable` spielt, und drei vorgegebenen Funktionen, die auf einer vierten, +abstrakten basieren. Die Methoden für Gleichheit und Ungleichheit erscheinen hier nicht, da sie bereits in allen Objekten von Scala vorhanden sind. Der Typ `Any`, welcher oben verwendet wurde, stellt den Ober-Typ aller Typen in Scala dar. Er kann @@ -504,7 +512,7 @@ als noch allgemeinere Version von Javas `Object` angesehen werden, da er außerd Basis-Typen wie `Int` und `Float` ist. Um Objekte einer Klasse vergleichen zu können, ist es also hinreichend, Gleichheit und die -kleiner-gleich-Beziehung zu implementieren, und dieses Verhalten gewissermaßen mit der eigentlichen +kleiner-als-Beziehung zu implementieren, und dieses Verhalten gewissermaßen mit der eigentlichen Klasse zu vermengen (mix in). Als Beispiel soll eine Klasse für Datumsangaben dienen, die Daten eines gregorianischen Kalenders repräsentiert. Solche Daten bestehen aus Tag, Monat und Jahr, welche durch Ganzzahlen dargestellt werden: @@ -513,14 +521,15 @@ durch Ganzzahlen dargestellt werden: def year = y def month = m def day = d + override def toString = year + "-" + month + "-" + day -Der wichtige Teil dieser Definition ist die `extends Ord` Deklaration, welche dem Namen der Klasse -und den Parametern folgt. Er sagt aus, dass `Date` vom Trait `Ord` erbt. +Der wichtige Teil dieser Definition ist die Deklaration `extends Ord`, welche dem Namen der Klasse +und deren Parametern folgt. Sie sagt aus, dass `Date` vom Trait `Ord` erbt. Nun folgt eine Re-Implementierung der Methode `equals`, die von `Object` geerbt wird, so dass die Daten korrekt nach ihren Feldern verglichen werden. Die vorgegebene Implementierung von `equals` ist -dafür nicht nützlich, da in Java Objekte physisch, also nach deren Adresse im Speicher verglichen +dafür nicht nützlich, da in Java Objekte physisch, also nach deren Adressen im Speicher, verglichen werden. Daher verwenden wir folgende Definition: override def equals(that: Any): Boolean = @@ -529,13 +538,13 @@ werden. Daher verwenden wir folgende Definition: o.day == day && o.month == month && o.year == year } -Diese Methode verwendet die vordefinierte Methoden `isInstanceOf` und `asInstanceOf`. Erstere -entspricht Javas `instanceof`-Operator und gibt `true` zurück, genau wenn das zu testende Objekt -eine Instanz des angegebenen Typs ist. Letztere entspricht Javas Operator für Typ-Umwandlungen (cast -operator): ist das Objekt eine Instanz des angegebenen Typs, kann es als solcher angesehen und -gehandhabt werden, ansonsten wird eine `ClassCastException` ausgelöst. +Diese Methode verwendet die vordefinierten Methoden `isInstanceOf` und `asInstanceOf`. Erstere +entspricht Javas `instanceof`-Operator und gibt `true` zurück, wenn das zu testende Objekt eine +Instanz des angegebenen Typs ist. Letztere entspricht Javas Operator für Typ-Umwandlungen (cast): +ist das Objekt eine Instanz des angegebenen Typs, kann es als solcher angesehen und gehandhabt +werden, ansonsten wird eine `ClassCastException` ausgelöst. -Schließlich kann die letzte Methode definiert werden, die für `Ord` notwendig ist und die +Schließlich kann die letzte Methode definiert werden, die für `Ord` notwendig ist, und die kleiner-als-Beziehung implementiert. Diese nutzt eine andere, vordefinierte Methode, namens `error`, des Paketes `sys`, welche eine `RuntimeException` mit der angegebenen Nachricht auslöst. @@ -555,14 +564,14 @@ sowohl Daten als auch vergleichbare Objekte dar. Vielmehr implementiert diese Kl Methoden, die für das Vergleichen von Objekten notwendig sind: `equals` und `<`, die direkt in der Definition von `Date` vorkommen, sowie die anderen, in dem Trait `Ord` definierten Methoden. -Traits sind nützlich in Situationen wie der obigen, deren vollen Funktionsumfang hier zu zeigen, -würde allerdings den Rahmen dieses Dokumentes sprengen. +Traits sind nützlich in Situationen wie der obigen, den vollen Funktionsumfang hier zu zeigen, würde +allerdings den Rahmen dieses Dokumentes sprengen. ## Generische Programmierung -Eine weitere Charakteristik Scalas, die in dieser Anleitung vorgestellt wird, behandelt das Konzept -der generischen Programmierung. Java-Programmierer, die die Sprache noch vor der Version 1.5 kennen, -sollten mit den Problemen vertraut sein, die auftreten, wenn generische Programmierung nicht +Eine weitere Charakteristik Scalas, die in diesem Tutorial vorgestellt werden soll, behandelt das +Konzept der generischen Programmierung. Java-Programmierer, die die Sprache noch vor der Version 1.5 +kennen, sollten mit den Problemen vertraut sein, die auftreten, wenn generische Programmierung nicht unterstützt wird. Generische Programmierung bedeutet, Quellcode nach Typen zu parametrisieren. Beispielsweise stellt @@ -574,7 +583,8 @@ verwenden. Diese willkürliche Wahl wäre sehr einschränkend. Aufgrund dieser Probleme griff man in Java vor der Einführung der generischen Programmierung zu dem Mittel, `Object`, den Ober-Typ aller Typen, als Element-Typ zu verwenden. Diese Lösung ist allerdings auch weit entfernt von Eleganz, da sie sowohl ungeeignet für die Basis-Typen, wie `int` -oder `float`, ist als auch viele Typ-Umwandlungen für den nutzenden Programmierer bedeutet. +oder `float`, ist, als auch viele explizite Typ-Umwandlungen für den nutzenden Programmierer +bedeutet. Scala ermöglicht es, generische Klassen und Methoden zu definieren, um diesen Problemen aus dem Weg zu gehen. Für die Demonstration soll ein einfacher, generischer Container als Referenz-Typ dienen, @@ -582,18 +592,22 @@ der leer sein kann, oder auf ein Objekt des generischen Typs zeigt: class Reference[T] { private var contents: T = _ - def set(value: T) { contents = value } + def get: T = contents + + def set(value: T) { + contents = value + } } Die Klasse `Reference` ist anhand des Types `T` parametrisiert, der den Element-Typ repräsentiert. Dieser Typ wird im Körper der Klasse genutzt, wie bei dem Feld `contents`. Dessen Argument wird durch die Methode `get` abgefragt und mit der Methode `set` verändert. -Der obige Quellcode führt Variablen in Scala ein, welche keiner weiteren Erklärung erfordern -sollten. Schon interessanter ist der initiale Wert dieser Variablen, der mit `_` gekennzeichnet -wurde. Dieser Standardwert ist für numerische Typen `0`, `false` für Wahrheitswerte, `()` für den -Typ `Unit` und `null` für alle anderen Typen. +Der obige Quellcode führt veränderbare Variablen in Scala ein, welche keiner weiteren Erklärung +erfordern sollten. Schon interessanter ist der initiale Wert dieser Variablen, der mit `_` +gekennzeichnet wurde. Dieser Standardwert ist für numerische Typen `0`, `false` für Wahrheitswerte, +`()` für den Typ `Unit` und `null` für alle anderen Typen. Um diese Referenz-Klasse zu verwenden, muss der generische Typ bei der Erzeugung einer Instanz angegeben werden. Für einen Ganzzahl-Container soll folgendes Beispiel dienen: @@ -607,8 +621,8 @@ angegeben werden. Für einen Ganzzahl-Container soll folgendes Beispiel dienen: } Wie in dem Beispiel zu sehen ist, muss der Wert, der von der Methode `get` zurückgegeben wird, nicht -umgewandelt werden, wenn er als Ganzzahl verwendet werden soll. Es wäre außerdem nicht möglich, ein -Wert, der keine Ganzzahl ist, in einem solchen Container zu speichern, da er speziell und +umgewandelt werden, wenn er als Ganzzahl verwendet werden soll. Es wäre außerdem nicht möglich, +einen Wert, der keine Ganzzahl ist, in einem solchen Container zu speichern, da er speziell und ausschließlich für Ganzzahlen erzeugt worden ist. ## Zusammenfassung