Introduction
Es ist eine direkte Migration des Dwango-Tutorials, in dem Sie es studieren, bearbeiten und durch Ihre eigenen Begriffe ersetzen.
Lassen Sie uns nun über Klassendefinitionen in Scala sprechen. Bitte beachten Sie, dass davon ausgegangen wird, dass Sie die Java-Klasse kennen.
Klassen in Scala unterscheiden sich nur in Notation von Java-Sprachklassen. Die Klassendefinition von Scala hat folgende Form:
class <Name der Klasse> '(' (<Argumentname 1> : <Argumenttyp 1>, <Argumentname 2>: <Argumenttyp 2> ...)? ')' {
(<Felddefinition> | <Methodendefinition> )*
}
Angenommen, Sie möchten einen Klassenpunkt definieren, der einen Punkt darstellt. Angenommen, Point besteht aus einem Feld x (Int-Typ) und einem Feld y (Int-Typ), die x-Koordinaten darstellen. Das Schreiben dieser Klasse Point in Scala sieht folgendermaßen aus:
class Point(_x: Int, _y: Int) {
val x = _x
val y = _y
}
Wenn Sie ein Feld mit demselben Namen wie das Konstruktorargument definieren und verfügbar machen möchten, können Sie es wie folgt kurz schreiben.
class Point(val x: Int, val y: Int)
Unmittelbar nach dem Klassennamen befindet sich eine Konstruktorargumentdefinition Mit val / var können Konstruktorargumente als Felder verfügbar gemacht werden Beachten Sie den Punkt. Zunächst verwendet Scala nur einen Konstruktor pro Klasse. Scala behandelt diesen Konstruktor speziell als primären Konstruktor. Obwohl es grammatikalisch möglich ist, mehrere Konstruktoren zu definieren, wird es in der Praxis selten verwendet. Wenn Sie eine Möglichkeit zum Erstellen mehrerer Objekte bereitstellen möchten, definieren Sie diese häufig als Apply-Methode eines Objekts.
Der zweite Punkt ist, dass, wenn Sie dem Argument des primären Konstruktors val / var hinzufügen, das Feld verfügbar gemacht wird und von außen zugegriffen werden kann. Beachten Sie, dass sich der Umfang der Argumente des primären Konstruktors auf die gesamte Klassendefinition erstreckt. Daher können Sie wie unten gezeigt direkt aus der Methodendefinition auf das Konstruktorargument verweisen.
class Point(val x: Int, val y: Int) {
def +(p: Point): Point = {
new Point(x + p.x, y + p.y)
}
override def toString(): String = "(" + x + ", " + y + ")"
}
Die + -Methodendefinition wurde bereits früher als Beispiel für die Methodendefinition aufgeführt, hat jedoch im Allgemeinen die folgende Form.
(private([this | <Paketnamen>])? | protected([<Paketnamen>])?)? def <Methodenname> '('
(<Argumentname> :Argumenttyp(, Argumentname : <Argumenttyp>)*)?
')': <Rückgabetyp> = <Körper>
In der Praxis wird häufig die folgende Form unter Verwendung eines Blockausdrucks angenommen.
(private([this | <Paketnamen>])? | protected([<Paketnamen>])?)? def <Methodenname> '('
(<Argumentname> :Argumenttyp(, Argumentname : <Argumenttyp>)*)?
')': <Rückgabetyp> = {
(<Formel> (; | <Neue Zeile>)?)*
}
Dies ist nur dann der Fall, wenn der Methodenkörper aus einem Blockausdruck besteht und es keine dedizierte Syntax zum Einschließen der Methodendefinition in {} gibt.
Selbst wenn Sie den Rückgabetyp weglassen, wird der Typ außer in besonderen Fällen abgeleitet. Machen Sie es sich jedoch zur besseren Lesbarkeit zur Gewohnheit, den Rückgabetyp anzugeben. Mit private kann auf die Methode nur innerhalb dieser Klasse zugegriffen werden, und mit protected kann nur auf abgeleitete Klassen zugegriffen werden. Mit private [this] kann nur von demselben Objekt aus darauf zugegriffen werden. Wenn Sie einen privaten [Paketnamen] hinzufügen, können Sie nur von denjenigen aus zugreifen, die zu demselben Paket gehören, und wenn Sie einen geschützten [Paketnamen] hinzufügen, können Sie zusätzlich zu der abgeleiteten Klasse von allen Benutzern darauf zugreifen, die zu demselben Paket gehören. Wird sein. Wenn weder privat noch geschützt angehängt ist, wird die Methode als öffentlich betrachtet.
Verwenden wir die zuvor in REPL definierte Point-Klasse.
scala> class Point(val x: Int, val y: Int) {
| def +(p: Point): Point = {
| new Point(x + p.x, y + p.y)
| }
| override def toString(): String = "(" + x + ", " + y + ")"
| }
defined class Point
scala> val p1 = new Point(1, 1)
p1: Point = (1, 1)
scala> val p2 = new Point(2, 2)
p2: Point = (2, 2)
scala> p1 + p2
res0: Point = (3, 3)
Die Methode kann so definiert werden, dass sie mehrere Argumentlisten enthält:
(private([this | <Paketnamen>])? | protected([<Paketnamen>])?)? def <Methodenname> '('
(<Argumentname> :Argumenttyp(, Argumentname : <Argumenttyp>)*)?
')'( '('
(<Argumentname> :Argumenttyp(, Argumentname : <Argumenttyp>)*)?
')' )* : <Rückgabetyp> = <Körpertyp>
Methoden mit mehreren Argumentlisten können in Kombination mit der zuckerbeschichteten Syntax von Scala verwendet werden, um fließende APIs zu erstellen, die für die unten beschriebenen impliziten Parameter benötigt werden, oder um die Typinferenz zu unterstützen. .. Definieren wir eine Additionsmethode mit mehreren Argumentlisten.
scala> class Adder {
| def add(x: Int)(y: Int): Int = x + y
| }
defined class Adder
scala> val adder = new Adder()
adder: Adder = Adder@55f0d6ee
scala> adder.add(2)(3)
res1: Int = 5
scala> val fun = adder.add(2) _
fun: Int => Int = $$Lambda$7309/854803072@56ddbb41
scala> fun(3)
res2: Int = 5
Methoden mit mehreren Argumentlisten werden in der Form obj.method (x) (y) anstelle der Form obj.method (x, y) aufgerufen. Sie können auch eine neue Funktion (Teilanwendung) erstellen, indem Sie nur das erste Argument wie im Beispiel unten anwenden.
Sie können auch eine Methode erstellen, die einfach mehrere Argumente enthält, ohne eine Liste mit mehreren Argumenten zu verwenden:
scala> class Adder {
| def add(x: Int, y: Int): Int = x + y
| }
defined class Adder
scala> val adder = new Adder()
adder: Adder = Adder@2fb31283
scala> adder.add(2, 3)
res3: Int = 5
scala> val fun: Int => Int = adder.add(2, _)
fun: Int => Int = $$Lambda$7310/1712767810@7dbbe2d8
scala> fun(3)
res4: Int = 5
Die Felddefinition lautet
(private([this | <Paketnamen>])? | protected([<Paketnamen>])?)? (val | var) <Feldname>: <Feldtyp> = <Initialisierungsformel>
Es hat die Form von. Wenn es val ist, ist es ein unveränderliches Feld, und wenn es var ist, ist es ein veränderliches Feld. Wenn privat hinzugefügt wird, kann auf das Feld nur innerhalb der Klasse zugegriffen werden, und wenn geschützt hinzugefügt wird, kann auf das Feld nur von der abgeleiteten Klasse der Klasse zugegriffen werden. Wenn Sie private [this] hinzufügen, kann nur von demselben Objekt aus darauf zugegriffen werden. Wenn Sie private [
Zu diesem Zeitpunkt können Sie möglicherweise keine Implementierung schreiben, und Sie möchten möglicherweise beim Erben eine Implementierung einer Methode oder eines Felds angeben, die später beschrieben wird. Um solche Fälle zu behandeln, können Sie mit Scala abstrakte Elemente definieren. Abstrakte Mitglieder können Methoden oder Felder sein und für Methoden:
(private([this | <Paketnamen>])? | protected([<Paketnamen>])?)? def <Methodenname> '('
(<Argumentname> :Argumenttyp(, Argumentname : <Argumenttyp>)*)?
')': <Rückgabetyp>
Die Felddefinition sieht folgendermaßen aus:
(private([this | <Paketnamen>])? | protected([<Paketnamen>])?)? (val | var) <Feldname>: <Feldtyp>
Es ist dasselbe wie eine normale Methode oder Felddefinition, außer dass es keinen Inhalt der Methode oder des Feldes gibt. Außerdem muss eine Klasse mit einer oder mehreren abstrakten Methoden als abstrakte Klasse deklariert werden. Beispielsweise wird eine abstrakte Klasse XY mit x- und y-Koordinaten wie folgt definiert: Der Punkt ist, dass Sie der Klasse den abstrakten Modifikator voranstellen müssen.
scala> abstract class XY {
| def x: Int
| def y: Int
| }
defined class XY
Scala-Klassen können genau wie Java-Klassen vererbt werden. Vererbung hat zwei Zwecke. Eine besteht darin, die Implementierung wiederzuverwenden, indem die Implementierung der Oberklasse in der Unterklasse durch Vererbung verwendet wird. Das andere ist, dass mehrere Unterklassen die Schnittstelle einer gemeinsamen Oberklasse erben, um die Verarbeitung zu standardisieren1. Es ist bekannt, dass die Implementierungsvererbung Probleme wie das Verhalten aufweist, wenn Methoden- und Feldnamen aufgrund mehrerer Vererbungen in Konflikt stehen, und Java die Implementierungsvererbung auf nur eine beschränkt. Java 8 ermöglicht Schnittstellen eine Standardimplementierung, jedoch mit der Einschränkung, dass Variablen dies nicht können. Scala verwendet einen Mechanismus namens Merkmale, um mehrere Implementierungen zu erben. Merkmale werden jedoch in einem separaten Abschnitt erläutert.
Dieser Abschnitt beschreibt die Vererbung normaler Scala-Klassen. Die Klassenvererbung in Scala hat die folgende Syntax:
class <Name der Klasse> <Klassenargument> (extends <Super Klasse>)? (with <Trate Name>)* {
(<Felddefinition> | <Methodendefinition>)*
}
Der Name des Merkmals wird hier nicht verwendet, wird aber später im Abschnitt über das Merkmal erläutert. Die Vererbung funktioniert genauso wie Java-Klassen, außer dass Sie das Schlüsselwort override verwenden müssen, wenn Sie vorhandene Methoden überschreiben. Sie können beispielsweise Folgendes tun:
scala> class APrinter() {
| def print(): Unit = {
| println("A")
| }
| }
defined class APrinter
scala> class BPrinter() extends APrinter {
| override def print(): Unit = {
| println("B")
| }
| }
defined class BPrinter
scala> new APrinter().print
A
scala> new BPrinter().print
B
Wenn Sie das Überschreibungsschlüsselwort hier entfernen, wird die folgende Meldung ausgegeben und ein Kompilierungsfehler tritt auf.
scala> class BPrinter() extends APrinter {
| def print(): Unit = {
| println("B")
| }
| }
<console>:14: error: overriding method print in class APrinter of type ()Unit;
method print needs `override' modifier
def print(): Unit = {
^
Java macht häufig den Fehler, eine neue Methode zu definieren, um eine vorhandene Methode unbemerkt zu überschreiben. Scala verwendet jedoch das Schlüsselwort override, um dieses Problem auf Sprachebene zu beheben.
Nächstes Mal werden wir Objekte untersuchen.
Dieses Dokument ist CC BY-NC-SA 3.0
Es wird unter verteilt.
https://dwango.github.io/scala_text/
Recommended Posts