Scala Language
사례 클래스
수색…
통사론
- case 클래스 Foo () // 매개 변수가없는 사례 클래스는 빈 목록을 가져야합니다
- case 클래스 Foo (a1 : A1, ..., aN : AN) // a1 ... aN 필드를 갖는 case 클래스를 만든다.
- case 객체 바 // 싱글 톤 case 클래스 생성
사례 클래스 평등
사례 클래스별로 무료로 제공되는 기능 중 하나는 개체의 참조 평등을 검사하는 대신 모든 개별 멤버 필드의 값이 같은지 확인하는 자동 생성 equals
메서드입니다.
일반 수업 :
class Foo(val i: Int)
val a = new Foo(3)
val b = new Foo(3)
println(a == b)// "false" because they are different objects
사례 클래스 사용 :
case class Foo(i: Int)
val a = Foo(3)
val b = Foo(3)
println(a == b)// "true" because their members have the same value
생성 된 코드 아티팩트
case
변경자는 스칼라 컴파일러가 클래스에 대한 공통적 인 상용구 코드를 자동으로 생성하도록합니다. 이 코드를 수동으로 구현하는 것은 지루하고 오류의 원인입니다. 다음 케이스 클래스 정의 :
case class Person(name: String, age: Int)
... 다음 코드가 자동으로 생성됩니다.
class Person(val name: String, val age: Int)
extends Product with Serializable
{
def copy(name: String = this.name, age: Int = this.age): Person =
new Person(name, age)
def productArity: Int = 2
def productElement(i: Int): Any = i match {
case 0 => name
case 1 => age
case _ => throw new IndexOutOfBoundsException(i.toString)
}
def productIterator: Iterator[Any] =
scala.runtime.ScalaRunTime.typedProductIterator(this)
def productPrefix: String = "Person"
def canEqual(obj: Any): Boolean = obj.isInstanceOf[Person]
override def hashCode(): Int = scala.runtime.ScalaRunTime._hashCode(this)
override def equals(obj: Any): Boolean = this.eq(obj) || obj match {
case that: Person => this.name == that.name && this.age == that.age
case _ => false
}
override def toString: String =
scala.runtime.ScalaRunTime._toString(this)
}
대 / case
수정자는 대등 객체도 생성합니다.
object Person extends AbstractFunction2[String, Int, Person] with Serializable {
def apply(name: String, age: Int): Person = new Person(name, age)
def unapply(p: Person): Option[(String, Int)] =
if(p == null) None else Some((p.name, p.age))
}
object
적용하면 대 / case
수정자는 비슷한 효과를 나타냅니다 (효과는 적지 만). 여기서 주요한 이득은 toString
구현과 여러 프로세스에서 일관된 hashCode
값입니다. 대 / 소문자의 객체는 참조 평등을 사용합니다 (정확하게).
object Foo extends Product with Serializable {
def productArity: Int = 0
def productIterator: Iterator[Any] =
scala.runtime.ScalaRunTime.typedProductIterator(this)
def productElement(i: Int): Any =
throw new IndexOutOfBoundsException(i.toString)
def productPrefix: String = "Foo"
def canEqual(obj: Any): Boolean = obj.isInstanceOf[this.type]
override def hashCode(): Int = 70822 // "Foo".hashCode()
override def toString: String = "Foo"
}
클래스 자체와 그 부속 오브젝트 모두에서 case
수식어가 제공하는 메소드를 수동으로 구현하는 것은 여전히 가능합니다.
사례 클래스 기본 사항
일반 클래스와 비교하여 사례 클래스 표기법은 다음과 같은 몇 가지 이점을 제공합니다.
모든 생성자 인수는
public
이며 초기화 된 객체에서 액세스 할 수 있습니다 (일반적으로 여기서는 설명하지 않습니다).case class Dog1(age: Int) val x = Dog1(18) println(x.age) // 18 (success!) class Dog2(age: Int) val x = new Dog2(18) println(x.age) // Error: "value age is not a member of Dog2"
toString
,equals
,hashCode
(속성에 기반),copy
,apply
및unapply
메소드를 구현합니다.case class Dog(age: Int) val d1 = Dog(10) val d2 = d1.copy(age = 15)
패턴 일치를위한 편리한 메커니즘을 제공합니다.
sealed trait Animal // `sealed` modifier allows inheritance within current build-unit only case class Dog(age: Int) extends Animal case class Cat(owner: String) extends Animal val x: Animal = Dog(18) x match { case Dog(x) => println(s"It's a $x years old dog.") case Cat(x) => println(s"This cat belongs to $x.") }
사례 클래스 및 Immutabilty
Scala 컴파일러는 기본적으로 매개 변수 목록의 모든 인수 앞에 val
을 val
입니다. 즉, 기본적으로 사례 클래스는 변경되지 않습니다. 각 매개 변수에는 접근 자 메서드가 있지만 mutator 메서드는 없습니다. 예 :
case class Foo(i: Int)
val fooInstance = Foo(1)
val j = fooInstance.i // get
fooInstance.i = 2 // compile-time exception (mutation: reassignment to val)
case 클래스의 매개 변수를 var
로 선언하면 기본 비헤이비어가 무시되고 case 클래스를 변경할 수 있습니다.
case class Bar(var i: Int)
val barInstance = Bar(1)
val j = barInstance.i // get
barInstance.i = 2 // set
case 클래스가 'mutable'인 또 다른 인스턴스는 case 클래스의 값이 변경 가능할 때입니다.
import scala.collection._
case class Bar(m: mutable.Map[Int, Int])
val barInstance = Bar(mutable.Map(1 -> 2))
barInstance.m.update(1, 3) // mutate m
barInstance // Bar(Map(1 -> 3)
여기에서 발생하는 '돌연변이'지도에 유의 m
하지 않도록에 포인트를 m
자체. 따라서, 어떤 다른 객체가 멤버로서 m
을 가졌다면, 변화를 볼 수있을 것이다. 방법 다음 예제 변경 참고 instanceA
도 변경 instanceB
:
import scala.collection.mutable
case class Bar(m: mutable.Map[Int, Int])
val m = mutable.Map(1 ->2)
val barInstanceA = Bar(m)
val barInstanceB = Bar(m)
barInstanceA.m.update(1,3)
barInstanceA // Bar = Bar(Map(1 -> 3))
barInstanceB // Bar = Bar(Map(1 -> 3))
m // scala.collection.mutable.Map[Int,Int] = Map(1 -> 3)
특정 변경 사항이있는 객체의 복사본 만들기
사례 클래스는 특정 변경 사항을 사용하여 이전 필드와 동일한 필드를 공유하는 새 개체를 만드는 copy
메서드를 제공합니다.
이 기능을 사용하여 이전과 동일한 특성을 가진 새 객체를 만들 수 있습니다. 이 간단한 사례 클래스는이 기능을 보여줍니다.
case class Person(firstName: String, lastName: String, grade: String, subject: String)
val putu = Person("Putu", "Kevin", "A1", "Math")
val mark = putu.copy(firstName = "Ketut", lastName = "Mark")
// mark: People = People(Ketut,Mark,A1,Math)
이 예제에서 두 객체는 copy ( firstName
및 lastName
)에 지정된 위치를 제외하고 유사한 특성 ( grade = A1
, subject = Math
)을 공유한다는 것을 알 수 있습니다.
형식 안전을위한 단일 요소 사례 클래스
타입 안전성을 얻으려면 때때로 우리 도메인에서 원시 타입의 사용을 피하고 싶습니다. 예를 들어, name
가진 Person
을 상상해보십시오. 통상, name
을 String
로서 encode합니다. 다만, Person
의 name
을 나타내는 String
와 에러 메세지를 나타내는 String
를 혼용하는 것은 어렵지 않습니다.
def logError(message: ErrorMessage): Unit = ???
case class Person(name: String)
val maybeName: Either[String, String] = ??? // Left is error, Right is name
maybeName.foreach(logError) // But that won't stop me from logging the name as an error!
이러한 함정을 피하기 위해 다음과 같이 데이터를 인코딩 할 수 있습니다.
case class PersonName(value: String)
case class ErrorMessage(value: String)
case class Person(name: PersonName)
PersonName
과 ErrorMessage
또는 일반 String
섞어 사용하면 코드가 컴파일되지 않습니다.
val maybeName: Either[ErrorMessage, PersonName] = ???
maybeName.foreach(reportError) // ERROR: tried to pass PersonName; ErrorMessage expected
maybeName.swap.foreach(reportError) // OK
그러나 이것은 우리가 이제 PersonName
컨테이너에 /에서 String
을 box / unbox해야하기 때문에 런타임 오버 헤드가 적다. 이를 피하기 위해 PersonName
및 ErrorMessage
값 클래스를 만들 수 있습니다.
case class PersonName(val value: String) extends AnyVal
case class ErrorMessage(val value: String) extends AnyVal