Programming/Scala

case class (케이스 클래스)

Data Engineer 2019. 11. 17. 16:19

스칼라에서는 case class라는 클래스를 제공합니다. case class는 클래스와 유사하지만 약간의 차이가 존재합니다. 기본적으로 불변이며 불변 데이터를 모델링하기에 좋습니다. 또한 개발자들이 이러한 모델링을 위해 작성해야 하는 보일러플레이트 코드(boilerplate code)를 제공해줍니다. 그럼 예제를 통해 살펴보겠습니다.

case class Book(id: Int, title: String, isbn: Long)

case class는 위와 같이 class 선언시 앞에 case라는 키워드를 붙여서 생성합니다. 그럼 위와 같이 생성을 하게 되면 어떤 코드가 나오는지 살펴보겠습니다. 위의 case class를 scalac로 컴파일한 후 javap 커맨드를 이용해서 디컴파일을 하면 아래의 코드를 확인할 수 있습니다.

public class Book implements scala.Product,scala.Serializable {
  public static scala.Option<scala.Tuple3<java.lang.Object, java.lang.String, java.lang.Object>> unapply(Book);
  public static Book apply(int, java.lang.String, long);
  public static scala.Function1<scala.Tuple3<java.lang.Object, java.lang.String, java.lang.Object>, Book> tupled();
  public static scala.Function1<java.lang.Object, scala.Function1<java.lang.String, scala.Function1<java.lang.Object, Book>>> curried();
  public int id();
  public java.lang.String title();
  public long isbn();
  public Book copy(int, java.lang.String, long);
  public int copy$default$1();
  public java.lang.String copy$default$2();
  public long copy$default$3();
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public Book(int, java.lang.String, long);
}

위에 코드에서 알 수 있듯이 case class는 아래의 목록의 것들을 자동으로 생성해줍니다.

 

  • case class 인자들의 getter methods
  • hashCode와 equals
  • copy method
  • apply와 unapply가 있는 동반 객체
  • toString method

동반 객체(Companion object)내에 있는 apply 메서드와 unapply 메서드는 2가지 기능을 제공합니다. 먼저 apply 메서드는 factory로써 case class 객체 생성 시 new 키워드를 생략해서 객체를 생성하게 해 줍니다.

val book = Book(1,"Programming In Scala",9788370017361L)
book: Book = Book(1,Programming In Scala,9788370017361)

unapply 메서드는 extractor로써 패턴 매칭에서 사용됩니다.

book match {
  case Book(1, "Programming In Scala", 9788370017361L) => print("Extractor")
  case _                                               => print("Failed!")
}

Extractor

case class의 파라미터들은 클래스 내의 멤버가 됩니다. 또한 모든 파라미터는 val로 선언되죠. val로 선언되기 때문에 getter만 제공해줍니다.

@ book.title
res3: String = "Programming In Scala"

@ book.title = "Scala for Impatient"
cmd4.sc:1: reassignment to val
val res4 = book.title = "Scala for Impatient"
                      ^

그러나 case class 파라미터에 var를 선언해주면 getter뿐만 아니라 setter도 제공해줍니다.

@ val book2 = Book(1,"Programming In Scala",9788370017361L)
book2: Book = Book(1, "Programming In Scala", 9788370017361L)

@ book2.title = "Scala for Impatient"

case class에 파라미터가 없는 경우에는 case class를 사용하지 않고 case object를 사용합니다.

@ case class Book
(console):1: case classes must have a parameter list; try 'case class Book()' or 'case object Book'
case class Book
               ^

@ case class Book()
defined class Book

copy 메소드는 전체 복사나 부분 복사 기능을 제공합니다. 사용법은 아래와 같습니다.

@ val fullCopiedObject = book.copy()
fullCopiedObject: cmd0.Book = Book(1, "Programming In Scala", 9788370017361L)

@ val partialCopiedObject = book.copy(title = "Scala for Impatient")
partialCopiedObject: cmd0.Book = Book(1, "Scala for Impatient", 9788370017361L)

equals 메소드는 값을 기준으로 비교를 수행합니다. == 마찬가지입니다. 내부적으로는 equals를 호출합니다.

@ book == fullCopiedObject
res10: Boolean = true

@ book.equals(fullCopiedObject)
res11: Boolean = true

@ book.eq(fullCopiedObject)
res12: Boolean = false

@ fullCopiedObject.ne(book)
res14: Boolean = true

hashCode 메소드는 scala.runtime.ScalaRunTime._hashCode를 호출합니다. 해당 hashCode는 murmur hash 알고리즘을 사용합니다. murmur hash에 대해서는 자세히 언급하지 않고 넘어가겠습니다. 알고리즘이 궁금하신 분들은 링크를 통해 확인하시기 바랍니다.

toString 메소드는 아래와 같이 의미 있는 결과가 출력되도록 재정의됩니다.

@ book.toString
res15: String = "Book(1,Programming In Scala,9788370017361)"

이번 포스트에서는 scala의 case class에 대해 알아보았습니다. case class는 일반 class와 비슷하지만 불변 데이터를 모델링할 때 더욱 편리하게 사용할 수 있도록 제공하는 기능입니다. case class 선언 시 어떠한 차이가 있는지 이해하고 개발을 하면 도움이 될 것이라고 생각합니다.