Suche…


Syntax

  • class MyClass{} // curly braces are optional here as class body is empty
  • class MyClassWithMethod {def method: MyClass = ???}
  • new MyClass() //Instantiate
  • object MyObject // Singleton object
  • class MyClassWithGenericParameters[V1, V2](vl: V1, i: Int, v2: V2)
  • class MyClassWithImplicitFieldCreation[V1](val v1: V1, val i: Int)
  • new MyClassWithGenericParameters(2.3, 4, 5) oder mit einem anderen Typ: new MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
  • class MyClassWithProtectedConstructor protected[my.pack.age](s: String)

Instanziieren von Klasseninstanzen

Eine Klasse in Scala ist ein "Entwurf" einer Klasseninstanz. Eine Instanz enthält den von dieser Klasse definierten Zustand und Verhalten. So deklarieren Sie eine Klasse:

class MyClass{}  // curly braces are optional here as class body is empty

Eine Instanz kann mit einem new Schlüsselwort instanziiert werden:

var instance = new MyClass()

oder:

var instance = new MyClass

Klammern sind in Scala optional zum Erstellen von Objekten aus einer Klasse mit einem Konstruktor ohne Argumente. Wenn ein Klassenkonstruktor Argumente akzeptiert:

class MyClass(arg : Int)       // Class definition
var instance = new MyClass(2)  // Instance instantiation
instance.arg                   // not allowed

Hier benötigt MyClass ein Int Argument, das nur intern für die Klasse verwendet werden kann. arg kann nicht außerhalb von MyClass zugegriffen werden, es sei denn, es ist als Feld deklariert:

class MyClass(arg : Int){ 
    val prop = arg  // Class field declaration
} 

var obj = new MyClass(2)
obj.prop     // legal statement

Alternativ kann es im Konstruktor als public deklariert werden:

class MyClass(val arg : Int)   // Class definition with arg declared public
var instance = new MyClass(2)  // Instance instantiation
instance.arg                   //arg is now visible to clients

Klasse ohne Parameter instanziieren: {} vs ()

Nehmen wir an, wir haben eine Klasse MyClass ohne Konstruktorargument:

class MyClass

In Scala können wir es mit folgender Syntax instanziieren:

val obj = new MyClass()

Oder wir schreiben einfach:

val obj = new MyClass

Bei Nichtbeachtung kann die optionale Klammer in einigen Fällen zu unerwartetem Verhalten führen. Angenommen, wir möchten eine Aufgabe erstellen, die in einem separaten Thread ausgeführt werden soll. Unten ist der Beispielcode:

val newThread = new Thread { new Runnable {
        override def run(): Unit = {
            // perform task
            println("Performing task.")
        }
      }
    }

newThread.start   // prints no output

Wir denken vielleicht, dass dieser Beispielcode, wenn er ausgeführt wird, die ausführende Performing task. druckt Performing task. , aber zu unserer Überraschung wird nichts gedruckt. Mal sehen, was hier passiert. Wenn du genauer hinschaust, haben wir geschweifte Klammern {} , gleich nach dem new Thread . Es wurde eine anonyme Klasse erstellt, die den Thread :

val newThread = new Thread {
  //creating anonymous class extending Thread
}

Im Rumpf dieser anonymen Klasse haben wir dann unsere Aufgabe definiert (wieder eine anonyme Klasse, die die Runnable Schnittstelle implementiert). Wir haben vielleicht gedacht, dass wir einen public Thread(Runnable target) verwendet haben, aber in der Tat (durch Ignorieren von optionalem () ) haben wir einen public Thread() public Thread(Runnable target) verwendet, bei dem nichts im Rumpf der run() Methode definiert ist. Um das Problem zu beheben, müssen wir Klammern anstelle von geschweiften Klammern verwenden.

val newThread = new Thread ( new Runnable {
        override def run(): Unit = {
            // perform task
            println("Performing task.")
        }
      }
    )

Mit anderen Worten, hier {} und () sind nicht austauschbar .

Singleton & Begleitobjekte

Singleton-Objekte

Scala unterstützt statische Member, jedoch nicht auf dieselbe Weise wie Java. Als Alternative dazu bietet Scala Singleton Objects an . Singleton-Objekte ähneln einer normalen Klasse, können jedoch nicht mit dem new Schlüsselwort instanziiert werden. Hier ist ein Beispiel für eine Singleton-Klasse:

object Factorial {
    private val cache = Map[Int, Int]()
    def getCache = cache
}

Beachten Sie, dass wir das object zur Definition des Singleton-Objekts (anstelle von 'class' oder 'trait') verwendet haben. Da Einzelobjekte nicht instanziiert werden können, können sie keine Parameter haben. Der Zugriff auf ein Singleton-Objekt sieht folgendermaßen aus:

Factorial.getCache() //returns the cache

Beachten Sie, dass dies genau wie der Zugriff auf eine statische Methode in einer Java-Klasse aussieht.

Begleitobjekte

In Scala können Einzelobjekte den Namen einer entsprechenden Klasse gemeinsam nutzen. In einem solchen Szenario wird das Einzelobjekt als Begleitobjekt bezeichnet . Unterhalb der Klasse wird beispielsweise Factorial definiert und darunter ein Begleitobjekt (auch Factorial ). Standardmäßig werden Begleitobjekte in derselben Datei wie ihre Begleitklasse definiert.

class Factorial(num : Int) {

  def fact(num : Int) : Int = if (num <= 1) 1 else (num * fact(num - 1))

  def calculate() : Int = {
    if (!Factorial.cache.contains(num)) {    // num does not exists in cache
      val output = fact(num) // calculate factorial
      Factorial.cache += (num -> output)     // add new value in cache
    }

    Factorial.cache(num)
  }
}

object Factorial {
  private val cache = scala.collection.mutable.Map[Int, Int]()
}

val factfive = new Factorial(5)
factfive.calculate  // Calculates the factorial of 5 and stores it
factfive.calculate  // uses cache this time
val factfiveagain = new Factorial(5)
factfiveagain.calculate  // Also uses cache

In diesem Beispiel verwenden wir einen privaten cache , um die Fakultät einer Zahl zu speichern, um Rechenzeit für wiederholte Zahlen zu sparen.

Das object Factorial ist hier ein Begleitobjekt und die class Factorial ist die entsprechende Begleitklasse. Begleitobjekte und Klassen können auf ihre private Mitglieder zugreifen. Im obigen Beispiel Factorial die Factorial Klasse auf das private cache Member des zugehörigen Objekts zu.

Beachten Sie, dass eine neue Instanziierung der Klasse immer noch dasselbe Begleitobjekt verwendet, sodass Änderungen an Member-Variablen dieses Objekts übernommen werden.

Objekte

Während Klassen eher wie Blaupausen sind, sind Objekte statisch (dh bereits instanziiert):

object Dog {
    def bark: String = "Raf"
}

Dog.bark() // yields "Raf"

Sie werden oft als Begleiter einer Klasse verwendet, und Sie können schreiben:

class Dog(val name: String) {

}

object Dog {
    def apply(name: String): Dog = new Dog(name)
}

val dog = Dog("Barky") // Object
val dog = new Dog("Barky") // Class

Instanztypüberprüfung

Typüberprüfung : variable.isInstanceOf[Type]

Mit Mustervergleich (in dieser Form nicht so nützlich):

variable match {
  case _: Type => true
  case _ => false
}

Sowohl isInstanceOf als auch Pattern Matching prüfen nur den Typ des Objekts, nicht dessen generischen Parameter (keine Typänderung), außer bei Arrays:

val list: List[Any] = List(1, 2, 3)             //> list  : List[Any] = List(1, 2, 3)

val upcasting = list.isInstanceOf[Seq[Int]]     //> upcasting  : Boolean = true

val shouldBeFalse = list.isInstanceOf[List[String]]
                                                //> shouldBeFalse  : Boolean = true

Aber

val chSeqArray: Array[CharSequence] = Array("a") //> chSeqArray  : Array[CharSequence] = Array(a)
val correctlyReified = chSeqArray.isInstanceOf[Array[String]]
                                              //> correctlyReified  : Boolean = false


val stringIsACharSequence: CharSequence = ""    //> stringIsACharSequence  : CharSequence = ""
  
val sArray = Array("a")                         //> sArray  : Array[String] = Array(a)
val correctlyReified = sArray.isInstanceOf[Array[String]]
                                                //> correctlyReified  : Boolean = true

//val arraysAreInvariantInScala: Array[CharSequence] = sArray
//Error: type mismatch;  found   : Array[String]  required: Array[CharSequence]
//Note: String <: CharSequence, but class Array is invariant in type T.
//You may wish to investigate a wildcard type such as `_ <: CharSequence`. (SLS 3.2.10)
//Workaround:
val arraysAreInvariantInScala: Array[_ <: CharSequence] = sArray
                                                //> arraysAreInvariantInScala  : Array[_ <: CharSequence] = Array(a)
  

val arraysAreCovariantOnJVM = sArray.isInstanceOf[Array[CharSequence]]
                                                //> arraysAreCovariantOnJVM  : Boolean = true

Typcasting : variable.asInstanceOf[Type]

Mit Musterabgleich :

variable match {
  case _: Type => true
}

Beispiele:

  val x = 3                                       //> x  : Int = 3
  x match {
    case _: Int => true//better: do something
    case _ => false
  }                                               //> res0: Boolean = true
  
  x match {
    case _: java.lang.Integer => true//better: do something
    case _ => false
  }                                               //> res1: Boolean = true
  
  x.isInstanceOf[Int]                             //> res2: Boolean = true
  
  //x.isInstanceOf[java.lang.Integer]//fruitless type test: a value of type Int cannot also be a Integer
  
  trait Valuable { def value: Int}
  case class V(val value: Int) extends Valuable
  
  val y: Valuable = V(3)                          //> y  : Valuable = V(3)
  y.isInstanceOf[V]                               //> res3: Boolean = true
  y.asInstanceOf[V]                               //> res4: V = V(3)

Anmerkung: Hierbei handelt es sich nur um das Verhalten in der JVM. Auf anderen Plattformen (JS, native) kann das Casting / die Überprüfung von Typen sich möglicherweise unterscheiden.

Konstrukteure

Primärer Konstruktor

In Scala ist der Hauptkonstruktor der Körper der Klasse. Auf den Klassennamen folgt eine Parameterliste, die die Konstruktorargumente darstellt. (Wie bei jeder Funktion kann eine leere Parameterliste weggelassen werden.)

class Foo(x: Int, y: String) {
    val xy: String = y * x
    /* now xy is a public member of the class */
}

class Bar {
    ...
}

Die Konstruktionsparameter einer Instanz sind außerhalb ihres Konstruktorkörpers nicht verfügbar, sofern sie nicht mit dem Schlüsselwort val als Instanzmitglied gekennzeichnet sind:

class Baz(val z: String) 
// Baz has no other members or methods, so the body may be omitted

val foo = new Foo(4, "ab")
val baz = new Baz("I am a baz")
foo.x // will not compile: x is not a member of Foo
foo.xy // returns "abababab": xy is a member of Foo
baz.z // returns "I am a baz": z is a member of Baz
val bar0 = new Bar
val bar1 = new Bar() // Constructor parentheses are optional here

Alle Operationen, die ausgeführt werden sollen, wenn eine Instanz eines Objekts instanziiert wird, werden direkt in den Hauptteil der Klasse geschrieben:

class DatabaseConnection
    (host: String, port: Int, username: String, password: String) {
    /* first connect to the DB, or throw an exception */
    private val driver = new AwesomeDB.Driver()
    driver.connect(host, port, username, password)
    def isConnected: Boolean = driver.isConnected
    ...
}

Beachten Sie, dass es als gute Praxis angesehen wird, so wenig Nebenwirkungen wie möglich in den Konstruktor einzufügen. Anstelle des obigen Codes sollten Sie über connect und disconnect nachdenken, sodass der Verbrauchercode für die Planung der E / A verantwortlich ist.

Hilfskonstruktoren

Eine Klasse kann zusätzliche Konstruktoren enthalten, die als "Hilfskonstruktoren" bezeichnet werden. Diese werden durch Konstruktordefinitionen in der Form def this(...) = e , wobei e einen anderen Konstruktor aufrufen muss:

class Person(val fullName: String) {    
  def this(firstName: String, lastName: String) = this(s"$firstName $lastName")
}

// usage:
new Person("Grace Hopper").fullName // returns Grace Hopper
new Person("Grace", "Hopper").fullName // returns Grace Hopper

Dies bedeutet, dass jeder Konstruktor einen anderen Modifikator haben kann: nur einige sind möglicherweise öffentlich verfügbar:

class Person private(val fullName: String) {    
  def this(firstName: String, lastName: String) = this(s"$firstName $lastName")
}

new Person("Ada Lovelace") // won't compile
new Person("Ada", "Lovelace") // compiles

Auf diese Weise können Sie steuern, wie der Verbrauchercode die Klasse instanziiert.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow