Recherche…


Syntaxe

  • 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) ou avec un type différent: new MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
  • class MyClassWithProtectedConstructor protected[my.pack.age](s: String)

Instancier des instances de classe

Une classe dans Scala est un «modèle» d'une instance de classe. Une instance contient l'état et le comportement définis par cette classe. Pour déclarer une classe:

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

Une instance peut être instanciée à l'aide du new mot clé:

var instance = new MyClass()

ou:

var instance = new MyClass

Les parenthèses sont facultatives dans Scala pour créer des objets à partir d'une classe qui possède un constructeur sans argument. Si un constructeur de classe prend des arguments:

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

Ici, MyClass nécessite un argument Int , qui ne peut être utilisé qu'en interne pour la classe. arg ne peut pas être accessible en dehors MyClass sauf si elle est déclarée comme un champ:

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

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

Sinon, il peut être déclaré public dans le constructeur:

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 d'instanciation sans paramètre: {} vs ()

Disons que nous avons une classe MyClass sans argument de constructeur:

class MyClass

En Scala, nous pouvons l'instancier en utilisant la syntaxe ci-dessous:

val obj = new MyClass()

Ou nous pouvons simplement écrire:

val obj = new MyClass

Mais, sans attention, dans certains cas, la parenthèse facultative peut produire un comportement inattendu. Supposons que nous voulions créer une tâche qui devrait s'exécuter dans un thread séparé. Voici le code exemple:

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

newThread.start   // prints no output

Nous pouvons penser que cet exemple de code, s’il est exécuté, imprime une Performing task. , mais à notre grande surprise, il n’imprimera rien. Voyons ce qui se passe ici. Si vous regardez de plus près, nous avons utilisé des accolades {} , juste après le new Thread . Il a créé une classe annonymous qui étend Thread :

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

Et puis, dans le corps de cette classe anonymes, nous avons défini notre tâche (créant à nouveau une classe annoncymée implémentant une interface Runnable ). On aurait donc pu penser que nous public Thread(Runnable target) constructeur public Thread(Runnable target) mais en fait (en ignorant optional () ) nous avons utilisé le constructeur public Thread() sans rien défini dans la méthode body of run() . Pour corriger le problème, nous devons utiliser des parenthèses au lieu d’accolades.

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

En d'autres termes, ici {} et () ne sont pas interchangeables .

Singleton & Companion Objects

Objets singleton

Scala prend en charge les membres statiques, mais pas de la même manière que Java. Scala fournit une alternative à cela appelé Singleton Objects . Les objets singleton sont similaires à une classe normale, sauf qu'ils ne peuvent pas être instanciés à l'aide du new mot clé. Voici un exemple de classe singleton:

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

Notez que nous avons utilisé le mot-clé object pour définir un objet singleton (au lieu de «class» ou «trait»). Comme les objets singleton ne peuvent pas être instanciés, ils ne peuvent pas avoir de paramètres. L'accès à un objet singleton ressemble à ceci:

Factorial.getCache() //returns the cache

Notez que cela ressemble exactement à l'accès à une méthode statique dans une classe Java.

Objets compagnons

Dans Scala, les objets singleton peuvent partager le nom d'une classe correspondante. Dans un tel scénario, l'objet singleton est appelé objet compagnon . Par exemple, sous la classe Factorial est défini et un objet compagnon (également appelé Factorial ) est défini en dessous. Par convention, les objets compagnons sont définis dans le même fichier que leur classe compagnon.

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

Dans cet exemple, nous utilisons un cache privé pour stocker la factorielle d'un nombre afin de gagner du temps de calcul pour des nombres répétés.

Ici, l' object Factorial est un objet compagnon et la class Factorial est la classe compagnon correspondante. Les objets compagnons et les classes peuvent accéder private membres private chacun. Dans l'exemple ci-dessus, la classe Factorial accède au membre de cache privé de son objet compagnon.

Notez qu'une nouvelle instanciation de la classe utilisera toujours le même objet compagnon, donc toute modification des variables membres de cet objet sera conservée.

Objets

Alors que les classes ressemblent plus à des plans, les objets sont statiques (c'est-à-dire déjà instanciés):

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

Dog.bark() // yields "Raf"

Ils sont souvent utilisés comme compagnons de cours, ils vous permettent d'écrire:

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

Vérification du type d'instance

Type de vérification : variable.isInstanceOf[Type]

Avec correspondance de motif (pas si utile dans cette forme):

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

isInstanceOf et la correspondance de modèle isInstanceOf uniquement le type de l'objet, pas son paramètre générique (pas de réification de type), sauf pour les tableaux:

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

Mais

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]

Avec correspondance de motif :

variable match {
  case _: Type => true
}

Exemples:

  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)

Remarque: il ne s’agit que du comportement sur la JVM, sur d’autres plates-formes (JS, native), le type casting / verification peut se comporter différemment.

Constructeurs

Constructeur principal

En Scala, le constructeur principal est le corps de la classe. Le nom de la classe est suivi d'une liste de paramètres, qui sont les arguments du constructeur. (Comme pour toute fonction, une liste de paramètres vide peut être omise.)

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

class Bar {
    ...
}

Les paramètres de construction d'une instance ne sont pas accessibles en dehors de son corps de constructeur, à moins qu'ils ne soient marqués comme membre d'instance par le mot clé 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

Toutes les opérations à effectuer lorsqu'une instance d'un objet est instanciée sont écrites directement dans le corps de la 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
    ...
}

Notez qu'il est conseillé de placer le moins d'effets secondaires possible dans le constructeur. au lieu du code ci-dessus, on devrait envisager d'avoir des méthodes de connect et de disconnect pour que le code consommateur soit responsable de la planification des entrées-sorties.

Constructeurs Auxiliaires

Une classe peut avoir des constructeurs supplémentaires appelés «constructeurs auxiliaires». Celles-ci sont définies par des définitions de constructeur sous la forme def this(...) = e , où e doit invoquer un autre constructeur:

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

Cela implique que chaque constructeur peut avoir un modificateur différent: seuls certains peuvent être disponibles publiquement:

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

De cette manière, vous pouvez contrôler la manière dont le code consommateur peut instancier la classe.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow