Zoeken…


Syntaxis

  • 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) of met een ander type: new MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
  • class MyClassWithProtectedConstructor protected[my.pack.age](s: String)

Instantieklasse-instanties

Een klasse in Scala is een 'blauwdruk' van een klasse-instantie. Een exemplaar bevat de status en het gedrag zoals gedefinieerd door die klasse. Een klasse aangeven:

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

Een exemplaar kan worden gestart met behulp van een new trefwoord:

var instance = new MyClass()

of:

var instance = new MyClass

Haakjes zijn optioneel in Scala voor het maken van objecten uit een klasse zonder constructor. Als een klassenbouwer argumenten aanneemt:

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

Hier vereist MyClass één Int argument, dat alleen intern voor de klas kan worden gebruikt. arg kan niet worden gebruikt buiten MyClass tenzij het als een veld wordt aangegeven:

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

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

Als alternatief kan het in de constructor openbaar worden gemaakt:

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 instantiëren zonder parameter: {} vs ()

Laten we zeggen dat we een klasse MyClass hebben zonder constructorargument:

class MyClass

In Scala kunnen we het instantiëren met behulp van onderstaande syntaxis:

val obj = new MyClass()

Of we kunnen eenvoudig schrijven:

val obj = new MyClass

Maar als er niet op wordt gelet, kan optionele haakjes in sommige gevallen onverwacht gedrag veroorzaken. Stel dat we een taak willen maken die in een afzonderlijke thread moet worden uitgevoerd. Hieronder staat de voorbeeldcode:

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

newThread.start   // prints no output

We denken misschien dat deze voorbeeldcode, indien uitgevoerd, de Performing task. zal afdrukken Performing task. , maar tot onze verbazing zal het niets afdrukken. Laten we kijken wat hier gebeurt. Als je beter kijkt, hebben we accolades {} , direct na de new Thread . Het creëerde een anonieme klasse die Thread uitbreidt:

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

En toen hebben we in de kern van deze anonieme klasse onze taak gedefinieerd (opnieuw een anonieme klasse maken die Runnable interface implementeert). Dus we hebben misschien gedacht dat we de public Thread(Runnable target) constructor hebben gebruikt, maar in feite (door optionele () negeren) hebben we de public Thread() constructor gebruikt zonder dat er iets is gedefinieerd in de methode body of run() . Om het probleem op te lossen, moeten we haakjes gebruiken in plaats van accolades.

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

Met andere woorden, hier {} en () zijn niet uitwisselbaar .

Singleton & begeleidende objecten

Singleton-objecten

Scala ondersteunt statische leden, maar niet op dezelfde manier als Java. Scala biedt een alternatief voor dit Singleton Objects . Singleton-objecten zijn vergelijkbaar met een normale klasse, behalve dat ze niet kunnen worden geïnstantieerd met het new trefwoord. Hieronder is een voorbeeld van een singleton-klasse:

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

Merk op dat we het object trefwoord hebben gebruikt om het singleton object te definiëren (in plaats van 'class' of 'trait'). Omdat singleton-objecten niet kunnen worden geïnstantieerd, kunnen ze geen parameters hebben. Toegang tot een singleton-object ziet er als volgt uit:

Factorial.getCache() //returns the cache

Merk op dat dit er precies zo uitziet als toegang krijgen tot een statische methode in een Java-klasse.

Begeleidende objecten

In Scala kunnen singleton-objecten de naam van een overeenkomstige klasse delen. In een dergelijk scenario wordt het singleton-object een begeleidend object genoemd . Onder de klasse Factorial wordt bijvoorbeeld gedefinieerd, en eronder wordt een begeleidend object (ook wel Factorial genoemd) gedefinieerd. Volgens conventie worden begeleidende objecten in hetzelfde bestand gedefinieerd als hun begeleidende klasse.

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 dit voorbeeld gebruiken we een cache om een faculteit van een getal op te slaan om de berekeningstijd voor herhaalde getallen te besparen.

Hier is object Factorial een begeleidend object en is class Factorial de bijbehorende begeleidende klasse. Companion-objecten en klassen hebt toegang tot elkaars private leden. In het bovenstaande voorbeeld heeft de Factorial klasse toegang tot het cache lid van het bijbehorende object.

Merk op dat een nieuwe instantie van de klasse nog steeds hetzelfde bijbehorende object gebruikt, dus elke wijziging in lidvariabelen van dat object zal worden overgenomen.

Voorwerpen

Terwijl klassen meer op blauwdrukken lijken, zijn objecten statisch (dat wil zeggen dat ze al zijn geïnstantieerd):

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

Dog.bark() // yields "Raf"

Ze worden vaak gebruikt als aanvulling op een klas, ze stellen je in staat om te schrijven:

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

Exemplaartype controleren

Type check : variable.isInstanceOf[Type]

Met patroonovereenkomst (niet zo handig in deze vorm):

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

Zowel isInstanceOf als patroonaanpassing controleert alleen het type van het object, niet de generieke parameter (geen typereificatie), behalve 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

Maar

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

Type casting : variable.asInstanceOf[Type]

Met patroonovereenkomst :

variable match {
  case _: Type => true
}

Voorbeelden:

  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)

Opmerking: dit gaat alleen over het gedrag op de JVM, op andere platforms (JS, native) type casten / controleren kan zich anders gedragen.

constructors

Primaire constructeur

In Scala is de primaire constructor het lichaam van de klas. De klassenaam wordt gevolgd door een parameterlijst, de constructorargumenten. (Zoals bij elke functie, kan een lege parameterlijst worden weggelaten.)

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

class Bar {
    ...
}

De constructieparameters van een instantie zijn niet toegankelijk buiten de hoofdtekst van de instantie, tenzij gemarkeerd als een instantie-lid met het trefwoord val :

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 bewerkingen die moeten worden uitgevoerd wanneer een instantie van een object wordt geïnstantieerd, worden rechtstreeks in de hoofdtekst van de klasse geschreven:

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
    ...
}

Merk op dat het als een goede praktijk wordt beschouwd om zo min mogelijk bijwerkingen in de constructor te brengen; in plaats van de bovenstaande code, zou men moeten overwegen connect en disconnect , zodat de consumentencode verantwoordelijk is voor het plannen van IO.

Hulpconstructeurs

Een klasse kan extra constructors hebben die 'hulpconstructers' worden genoemd. Deze worden gedefinieerd door constructeursdefinities in de vorm def this(...) = e , waarbij e een andere constructor moet aanroepen:

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

Dit houdt in dat elke constructor een andere modificator kan hebben: slechts enkele zijn openbaar beschikbaar:

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

Op deze manier kunt u bepalen hoe consumentencode de klasse kan instantiëren.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow