Sök…


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) eller med en annan typ: new MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
  • class MyClassWithProtectedConstructor protected[my.pack.age](s: String)

Instantiate Class Instances

En klass i Scala är en "ritning" av en klassinstans. En instans innehåller tillstånd och beteende som definieras av den klassen. Att förklara en klass:

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

En instans kan instanseras med new nyckelord:

var instance = new MyClass()

eller:

var instance = new MyClass

Parenteser är valfria i Scala för att skapa objekt från en klass som har en konstruktör utan argument. Om en klasskonstruktör tar argument:

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

Här kräver MyClass ett Int argument, som endast kan användas internt för klassen. arg kan inte nås utanför MyClass om det inte deklareras som ett fält:

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

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

Alternativt kan det förklaras offentligt i konstruktören:

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

Instantiating class utan parameter: {} vs ()

Låt oss säga att vi har en klass MyClass utan konstruktörargument:

class MyClass

I Scala kan vi instansera det med syntax nedan:

val obj = new MyClass()

Eller så kan vi helt enkelt skriva:

val obj = new MyClass

Men om inte uppmärksamhet kan i vissa fall valfritt parentes producera något oväntat beteende. Anta att vi vill skapa en uppgift som ska köras i en separat tråd. Nedan är provkoden:

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

newThread.start   // prints no output

Vi kanske tror att denna provkod om den körs kommer att skriva ut Performing task. , men till vår överraskning kommer det inte att skriva ut någonting. Låt oss se vad som händer här. Om du tittar närmare har vi använt lockiga hängslen {} , direkt efter new Thread . Det skapade en anonym klass som utökar Thread :

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

Och i kroppen av denna anonyma klass definierade vi vår uppgift (återigen att skapa en annonym klass som implementerar Runnable interface). Så vi kan ha trott att vi använt public Thread(Runnable target) konstruktören men i själva verket (genom att ignorera tillval () ) vi använt public Thread() konstruktör med ingenting definieras i kroppen av run() metoden. För att åtgärda problemet måste vi använda parenteser istället för lockiga hängslen.

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

Med andra ord är här {} och () inte utbytbara .

Singleton & Companion Objects

Singleton-objekt

Scala stöder statiska medlemmar, men inte på samma sätt som Java. Scala ger ett alternativ till detta kallas Singleton Objects . Singleton-objekt liknar en vanlig klass, förutom att de inte kan instanseras med det new nyckelordet. Nedan är ett exempel på singleton-klassen:

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

Notera att vi har använt object nyckelordet för att definiera singleton-objekt (i stället för 'klass' eller 'drag'). Eftersom singleton-objekt inte kan instanseras kan de inte ha parametrar. Att komma åt ett singleton-objekt ser så här ut:

Factorial.getCache() //returns the cache

Observera att det ser exakt ut som att komma åt en statisk metod i en Java-klass.

Companion Objects

I Scala kan singleton föremål dela namnet på motsvarande klass. I ett sådant scenario kallas singletonobjektet som ett följeslagningsobjekt . Till exempel definieras under klassen Factorial och ett följeslagareobjekt (även benämnt Factorial ) definieras under det. Genom konvention definieras följeslagare i samma fil som deras ledsagarklass.

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

I det här exemplet använder vi en privat cache att lagra fakultet för ett nummer för att spara beräkningstid för upprepade nummer.

Här är object Factorial ett följeslagareobjekt och class Factorial är dess motsvarande ledsagarklass. Companion-objekt och klasser kan komma åt varandras private medlemmar. I exemplet ovan får Factorial klassen åtkomst till den privata cache medlemmen i dess följeslagareobjekt.

Observera att en ny instansering av klassen fortfarande kommer att använda samma följeslagareobjekt, så varje ändring av medlemsvariabler för det objektet kommer att övergå.

Objekt

Medan klasser är mer som ritningar, är föremål statiska (dvs. redan instanserade):

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

Dog.bark() // yields "Raf"

De används ofta som följeslagare till en klass, de låter dig skriva:

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

Kontroll av förekomsttyp

Typkontroll : variable.isInstanceOf[Type]

Med mönstermatchning (inte så användbar i den här formen):

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

Både isInstanceOf och matchning av mönster kontrollerar bara objektets typ, inte dess generiska parameter (ingen typreifiering), med undantag för matriser:

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

Men

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

Typgjutning : variable.asInstanceOf[Type]

Med mönster matchning :

variable match {
  case _: Type => true
}

Exempel:

  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)

Anmärkning: Det här handlar bara om beteendet på JVM, på andra plattformar (JS, inbyggd) typgjutning / -kontroll kan fungera annorlunda.

konstruktörer

Primärkonstruktör

I Scala är den primära konstruktören klassens kropp. Klassnamnet följs av en parameterlista, som är konstruktörens argument. (Som med alla funktioner kan en tom parameterlista utelämnas.)

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

class Bar {
    ...
}

Konstruktionsparametrarna för en instans är inte tillgängliga utanför dess konstruktörskropp om de inte är markerade som en instansmedlem med val nyckelordet:

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

Alla operationer som ska utföras när en instans av ett objekt instanseras skrivs direkt i kroppen:

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

Observera att det anses vara god praxis att lägga så få biverkningar i konstruktören som möjligt; istället för koden ovan bör man överväga att använda connect och disconnect så att konsumentkoden är ansvarig för schemaläggning av IO.

Hjälpkonstruktörer

En klass kan ha ytterligare konstruktörer som kallas ”hjälpkonstruktörer”. Dessa definieras av konstruktordefinitioner i formen def this(...) = e , där e måste åberopa en annan konstruktör:

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

Detta innebär att varje konstruktör kan ha en annan modifierare: endast vissa kan vara tillgängliga offentligt:

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

På detta sätt kan du styra hur konsumentkoden kan instansera klassen.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow