수색…


통사론

  • 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) 또는 다른 유형의 new MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
  • class MyClassWithProtectedConstructor protected[my.pack.age](s: String)

클래스 인스턴스 인스턴스화

스칼라의 클래스는 클래스 인스턴스의 '청사진'입니다. 인스턴스에는 해당 클래스에서 정의한대로 상태와 동작이 포함됩니다. 수업을 선언하려면 다음 단계를 따르세요.

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

new 키워드를 사용하여 인스턴스를 인스턴스화 할 수 있습니다.

var instance = new MyClass()

또는:

var instance = new MyClass

Scala에서는 인수가없는 생성자가있는 클래스에서 객체를 만드는 경우 괄호를 사용하지 않아도됩니다. 클래스 생성자가 인수를 취하는 경우 :

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

여기에서 MyClass 는 내부적으로 만 사용할 수있는 Int 인수를 필요로합니다. arg 는 필드로 선언되지 않은 한 MyClass 외부에서 액세스 할 수 없습니다.

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

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

또는 생성자에서 public으로 선언 될 수 있습니다.

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

매개 변수없이 클래스 인스턴스화 : {} vs ()

생성자 인수가없는 MyClass 클래스가 있다고 가정 해 보겠습니다.

class MyClass

스칼라에서는 아래 구문을 사용하여 인스턴스를 생성 할 수 있습니다.

val obj = new MyClass()

또는 다음과 같이 작성할 수 있습니다.

val obj = new MyClass

그러나주의를 기울이지 않으면 옵션 괄호로 인해 예기치 않은 동작이 발생할 수 있습니다. 별도의 스레드에서 실행해야하는 작업을 만들고 싶다고 가정 해 보겠습니다. 샘플 코드는 다음과 같습니다.

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

newThread.start   // prints no output

우리는이 샘플 코드가 실행되면 Performing task. 을 인쇄 할 것이라고 생각할 수 있습니다 Performing task. , 놀랍게도, 그것은 아무 것도 인쇄하지 않을 것입니다. 여기서 무슨 일이 일어나는지 보자. 면밀히 살펴보면 new Thread 바로 뒤에 중괄호 {} 를 사용했습니다. 그것은 Thread 를 확장하는 익명 클래스를 만들었습니다 :

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

그리고이 익명 클래스의 본문에서 우리는 작업을 정의했습니다 (다시 Runnable 인터페이스를 구현하는 익명 클래스를 작성함). 그래서 우리는 우리가 사용하는 것을 생각했을 수도 public Thread(Runnable target) 생성자를하지만 사실 (무시 옵션 () ) 우리가 사용하는 public Thread() 의 본문에 정의 아무것도 생성자 run() 메소드를. 문제를 해결하려면 중괄호 대신 괄호를 사용해야합니다.

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

즉, 여기 {}()서로 바꿔 쓸 수 없습니다.

싱글 톤 및 컴패니언 객체

싱글 톤 객체

Scala는 정적 멤버를 지원하지만 Java와 같은 방식은 지원하지 않습니다. Scala는 Singleton Objects 라는 대안을 제공합니다. 싱글 톤 객체는 new 키워드를 사용하여 인스턴스화 할 수 없다는 점을 제외하고는 일반 클래스와 유사합니다. 다음은 샘플 싱글 톤 클래스입니다.

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

object 키워드를 사용하여 'class'또는 'trait'대신에 싱글 톤 객체를 정의했습니다. 싱글 톤 객체는 인스턴스화 될 수 없으므로 매개 변수를 가질 수 없습니다. 싱글 톤 객체에 액세스하는 것은 다음과 같습니다.

Factorial.getCache() //returns the cache

이것은 Java 클래스에서 정적 메서드에 액세스하는 것과 똑같은 모양입니다.

동반자 개체

스칼라에서 싱글 톤 객체는 해당 클래스의 이름을 공유 할 수 있습니다. 이러한 시나리오에서 싱글 톤 객체는 컴패니언 객체 로 참조됩니다. 예를 들어, Factorial 클래스가 정의되고, 그 아래에 Factorial 이라는 이름의 컴패니언 객체가 정의됩니다. 관습에 따라 컴패니언 객체는 컴패니언 클래스와 동일한 파일에 정의됩니다.

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

이 예제에서 우리는 반복되는 숫자의 계산 시간을 절약하기 위해 숫자의 계승을 저장하기 위해 개인 cache 를 사용합니다.

여기에서 object Factorial 은 동반 객체이며 class Factorial 은 해당 동반 클래스입니다. 동반자 객체와 클래스는 서로의 private 멤버에 액세스 할 수 있습니다. 위의 예에서 Factorial 클래스는 동반자 객체의 private cache 멤버에 액세스하고 있습니다.

클래스의 새로운 인스턴스 생성은 여전히 ​​동일한 컴패니언 객체를 사용하므로 해당 객체의 멤버 변수를 수정하면이 객체가 계속해서 전달됩니다.

사물

클래스는 청사진과 비슷하지만 객체는 정적입니다 (즉, 이미 인스턴스화 됨).

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

Dog.bark() // yields "Raf"

그들은 종종 클래스의 동반자로 사용되며, 다음과 같이 작성할 수 있습니다.

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

인스턴스 유형 검사

유형 검사 : variable.isInstanceOf[Type]

패턴 일치 (이 형식에서는 그렇게 유용하지 않음) :

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

isInstanceOf 와 패턴 일치 모두 배열을 제외하고 일반 매개 변수 (형식화 없음)가 아닌 개체의 형식 만 검사합니다.

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

그러나

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

유형 캐스팅 : variable.asInstanceOf[Type]

패턴 매칭 :

variable match {
  case _: Type => true
}

예 :

  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)

참고 : 이것은 JVM의 동작에 관한 것입니다. 다른 플랫폼 (JS, 네이티브) 유형 캐스팅 / 검사는 다르게 동작 할 수 있습니다.

생성자

기본 생성자

스칼라에서 기본 생성자는 클래스의 본문입니다. 클래스 이름 뒤에는 생성자 인수 인 매개 변수 목록이옵니다. (어떤 함수와 마찬가지로 빈 매개 변수 목록은 생략 될 수 있습니다.)

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

class Bar {
    ...
}

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

객체 인스턴스가 인스턴스화 될 때 수행되어야하는 모든 작업은 클래스 본문에 직접 작성됩니다.

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

가능한 한 생성자에 부작용을 거의주지 않는 것이 좋습니다. 위의 코드 대신 소비자 코드가 IO 스케줄링을 담당하도록 connectdisconnect 방법을 고려해야합니다.

보조 생성자

클래스에는 '보조 생성자'라고하는 추가 생성자가있을 수 있습니다. 이것은 def this(...) = e 형식의 생성자 정의에 의해 정의됩니다. 여기서 e 는 다른 생성자를 호출해야합니다.

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

이는 각 생성자가 다른 한정자를 가질 수 있음을 의미합니다. 일부만 공개적으로 사용할 수 있습니다.

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

이 방법으로 소비자 코드가 클래스를 인스턴스화하는 방법을 제어 할 수 있습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow