Ricerca…


Sintassi

  • 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) o con un tipo diverso: new MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
  • class MyClassWithProtectedConstructor protected[my.pack.age](s: String)

Istanziare istanze di classe

Una classe in Scala è un "progetto" di un'istanza di classe. Un'istanza contiene lo stato e il comportamento definiti da tale classe. Per dichiarare una classe:

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

Un'istanza può essere istanziata usando una new parola chiave:

var instance = new MyClass()

o:

var instance = new MyClass

Le parentesi sono facoltative in Scala per la creazione di oggetti da una classe che ha un costruttore senza argomenti. Se un costruttore di classi accetta argomenti:

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

Qui MyClass richiede un argomento Int , che può essere usato solo internamente alla classe. non è possibile accedere ad arg al di fuori di MyClass meno che non sia dichiarato come campo:

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

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

In alternativa, può essere dichiarato pubblico nel costruttore:

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

Classe di istanziazione senza parametro: {} vs ()

Diciamo che abbiamo una classe MyClass senza argomenti del costruttore:

class MyClass

In Scala possiamo istanziarlo usando la seguente sintassi:

val obj = new MyClass()

O possiamo semplicemente scrivere:

val obj = new MyClass

Ma, se non prestata attenzione, in alcuni casi la parentesi opzionale potrebbe produrre un comportamento inaspettato. Supponiamo di voler creare un'attività che dovrebbe essere eseguita in un thread separato. Di seguito è riportato il codice di esempio:

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

newThread.start   // prints no output

Potremmo pensare che questo codice di esempio, se eseguito, stamperà Performing task. , ma con nostra sorpresa, non stamperà nulla. Vediamo cosa sta succedendo qui. Se guardi più da vicino, abbiamo usato le parentesi graffe {} , subito dopo la new Thread . Ha creato una classe anonima che estende Thread :

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

E poi nel corpo di questa classe anonima, abbiamo definito il nostro compito (ancora una volta creando una classe anonima che implementa l'interfaccia Runnable ). Quindi potremmo aver pensato che abbiamo usato il costruttore public Thread(Runnable target) ma di fatto (ignorando facoltativo () ) abbiamo usato public Thread() costruttore public Thread() con niente definito nel metodo body of run() . Per correggere il problema, dobbiamo utilizzare le parentesi anziché le parentesi graffe.

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

In altre parole, qui {} e () non sono intercambiabili .

Singleton & Companion Objects

Oggetti Singleton

Scala supporta membri statici, ma non nello stesso modo di Java. Scala fornisce un'alternativa a questo chiamato Singleton Objects . Gli oggetti Singleton sono simili a una classe normale, tranne per il fatto che non possono essere istanziati utilizzando la new parola chiave. Di seguito è riportata una classe di esempio singleton:

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

Si noti che abbiamo usato la parola chiave object per definire l'oggetto singleton (invece di 'class' o 'trait'). Poiché gli oggetti singleton non possono essere istanziati, non possono avere parametri. L'accesso a un oggetto singleton ha il seguente aspetto:

Factorial.getCache() //returns the cache

Si noti che questo sembra esattamente come accedere a un metodo statico in una classe Java.

Companion Objects

In oggetti Singleton Scala può condividere il nome di una classe corrispondente. In uno scenario di questo tipo, l'oggetto singleton viene definito come un oggetto companion . Ad esempio, sotto la classe Factorial è definito, e sotto di esso è definito un oggetto companion (chiamato anche Factorial ). Per convenzione gli oggetti companion sono definiti nello stesso file della loro classe companion.

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 questo esempio utilizziamo una cache privata per memorizzare fattoriale di un numero per risparmiare tempo di calcolo per numeri ripetuti.

Qui object Factorial è un oggetto compagno e class Factorial è la corrispondente classe compagno. Gli oggetti e le classi Companion possono accedere ai rispettivi membri private . Nell'esempio sopra la classe Factorial sta accedendo al membro della cache privata dell'oggetto associato.

Si noti che una nuova istanziazione della classe continuerà a utilizzare lo stesso oggetto compagno, quindi qualsiasi modifica alle variabili membro di tale oggetto verrà trasferita.

Oggetti

Mentre le classi sono più simili a progetti, gli oggetti sono statici (cioè già istanziati):

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

Dog.bark() // yields "Raf"

Sono spesso usati come compagni di una classe, ti permettono di scrivere:

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

Controllo del tipo di istanza

Verifica del tipo : variable.isInstanceOf[Type]

Con la corrispondenza del modello (non così utile in questa forma):

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

Sia isInstanceOf che pattern matching stanno controllando solo il tipo dell'oggetto, non il suo parametro generico (nessuna reificazione del tipo), ad eccezione degli array:

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

Ma

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

Digitare casting : variable.asInstanceOf[Type]

Con la corrispondenza del modello :

variable match {
  case _: Type => true
}

Esempi:

  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)

Nota: si tratta solo del comportamento sulla JVM, su altre piattaforme (JS, native) il tipo di casting / controllo potrebbe comportarsi diversamente.

Costruttori

Costruttore primario

In Scala il costruttore principale è il corpo della classe. Il nome della classe è seguito da un elenco di parametri, che sono gli argomenti del costruttore. (Come con qualsiasi funzione, un elenco di parametri vuoto può essere omesso.)

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

class Bar {
    ...
}

I parametri di costruzione di un'istanza non sono accessibili al di fuori del suo corpo del costruttore a meno che non siano contrassegnati come membro di istanza dalla parola chiave 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

Qualsiasi operazione da eseguire quando un'istanza di un oggetto viene creata un'istanza viene scritta direttamente nel corpo della classe:

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

Si noti che è considerata una buona pratica inserire il minor numero possibile di effetti collaterali nel costruttore; al posto del codice precedente, si dovrebbe considerare di connect e disconnect metodi in modo che il codice del consumatore sia responsabile della pianificazione dell'IO.

Costruttori ausiliari

Una classe può avere costruttori aggiuntivi chiamati "costruttori ausiliari". Questi sono definiti dalle definizioni del costruttore nel formato def this(...) = e , dove e deve richiamare un altro costruttore:

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

Ciò implica che ogni costruttore può avere un modificatore diverso: solo alcuni possono essere disponibili pubblicamente:

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

In questo modo puoi controllare in che modo il codice del consumatore può istanziare la classe.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow