클래스
- 스칼라는 따로 생성자가 존재하지 않아, 클래스 바디 부분의 코드가 바로 실행
- 일반 클래스와 추상 클래스 모두 상속 가능 (extends), 추상 클래스 선언 (abstract)
- 추상 클래스는 매개변수를 가질 수 있고, 기본 메소드를 선언만 하거나 구현할 수 있음
scala> abstract class Person(name:String, age:Int) { // 추상 클래스
| def work
| def status(str:String)
| def greeting() = println(s"${name}님은 ${age}살 입니다.")
| }
scala> class Player(name:String, age:Int) extends Person(name, age) { // 상속
| def work = { println("일합니다.")}
| def status(str:String) = println(s"${str} 상태 입니다.")
| }
scala> var p = new Player("Cal", 10)
scala> p.work
일합니다.
scala> p.status("깨있는")
깨있는 상태 입니다.
- 클래스나 트레잇을 봉인 가능 (sealed)
- 봉인된 클래스와 하위 타입이 모두 한 파일에 있어야 하므로 관리 효율성이 높아짐
//file.scala
scala> sealed abstract class Furniture // 봉인 추상 클래스
scala> case class Couch() extends Furniture
scala> case class Chair() extends Furniture
//file2.scala
scala> case class Desk() extends Furniture
illegal inheritance from sealed class Furniture // 봉인 클래스를 이용하려면 한 파일 내에 같이 있어야 함
클래스 멤버변수
- 멤버 변수를 선언 시 가변/불변 변수를 명시적으로 선언 가능, 생략도 가능
기본 멤버 변수 | 가변 변수 var | 불변 변수 val |
---|
메소드 생성 X | getter, setter 메소드 생성 읽기, 쓰기 | getter 메소드 생성 |
클래스 내부에서만 사용 가능 | 읽기, 쓰기 | 읽기 |
scala> class Person(name:String, age:Int) // 클래스 선언
scala> val p = new Person("David", 30) // 클래스 생성
scala> class A // 멤버 변수 생략
scala> class Animal(name:String) { println(s"${name}") } // 기본 멤버 변수
scala> class Dog(var name:String) { println(s"${name}") } // 가변 변수
scala> class Cat(val name:String) { println(s"${name}") } // 불변 변수
scala> var ani = new Animal("동물")
scala> var dog = new Dog("개")
scala> var cat = new Cat("고양이") println(ani.name) // 기본 멤버 변수 접근 불가
error...
scala> println(dog.name)
개
scala> println(cat.name)
고양이
scala> dog.name = "바둑이"
dog.name: String = 바둑이
scala> cat.name = "나비" // 불변 변수 재할당 불가
error...
//멤버 변수는 종류 상관 없이 기본값 설정 가능
scala> class Person1(name:String, age:Int)
scala> class Person2(var name:String, var age:Int=10)
scala> class Person3(val name:String="Ted", val age:Int)
scala> var p1 = new Person1("David", 30)
scala> var p2 = new Person2("Tomphson")
scala> var p3 = new Person3("David", 12)
//name에 기본값을 입력할 수 없어서 error
scala> var p3 = new Person3(12)
error...
클래스 메소드
- 메소드 오버라이드 가능 (override)
- 클래스 생성 시 new를 이용해 메소드 재정의 가능
scala> class Person(name:String, age:Int, val job:String) {
| def greeting() = println(s"${name}님은 ${age}살 입니다.")
| def work() = println(s"직업은 ${job}입니다.")
| }
scala> class Writer(name:String, age:Int) extends Person(name, age, "") {
| override def work() = println(s"직업은 작가입니다.") // overrding
| }
scala> var w = new Writer("David", 15)
scala> w.greeting
David님은 15살 입니다.
scala> w.work
직업은 작가입니다.
scala> var p = new Person("David", 15, "학생") {
| override def work() = println(s"job is ${job}.")
| }
scala> p.greeting
David님은 15살 입니다.
scala> p.work
job is 학생.
케이스 클래스
- case를 이용해 선언, 인스턴스 생성할 때 new 사용하지 않아 초기화가 간단함
- 케이스 클래스는 불변 데이터로 멤버변수는 기본적으로 불변 변수로 선언됨
- 멤버 변수의 데이터를 이용해 데이터 비교
- toString, hashCode, equals, copy() 자동으로 생성
scala> case class Person(name:String, age:Int)
scala> var p = Person("A", 10)
scala> p.toString // toString
res0: String = Person(A, 10)
scala> p.hashCode // hashCode
res1: Int = 649684425
scala> var p1 = Person("B", 20)
scala> p.equal(p1) // equal
res2: Boolean = false
scala> p1 = p.copy() // copy()
p1: Persion = Person("A", 10)
패턴 매칭
- 파라미터 값과 case문의 값을 비교한 값의 결과 반환
// 기본 문법
param match {
case value1 => "value1" // param 값과 value1 값을 비교해 일치하는 값의 결과 반환
case _ => "default value" // 언더바_는 어떤 값도 일치하지 않을 때 결과 반환
}
scala> def matching(x:Int): String = x match { // 파라미터 x와 case문의 값들을 비교해 일치하는 값 반환
| case 0 => "zero"
| case 1 => "one"
| case _ => "many"
| }
scala> matching(1)
res0: String = one
scala> matching('A')
res1: String = many
- 케이스 클래스의 멤버 변수 값을 이용한 패턴 매칭 (일반 클래스는 패턴 매칭에 사용 불가)
- if <논리 표현>문을 이용해 패턴 처리를 구체화하는 방법인 패턴 가드 이용 가능
scala> abstract class Notification
scala> case class Email(sender:String, title:String, body:String) extends Notification
scala> case class SMS(caller:String, message:String) extends Notification
scala> case class VoiceRecording(contactName:String, link:String) extends Notification
scala> def showNotification(nofitication:Notification): String = {
| notification match {
| case Email(email, title, _) => s"You got an email from $email with title: $title" // body는 반환값에 사용하지 않아 _로 처리 가능
| case SMS(number, message) => s"You got an SMS from $number! Message: $message"
| case VoiceRecording(name, link) => s"You received a Voice Recording from $name! Click the link to hear it: $link"
| }
| }
scala> def showImportantNotification(nofitication:Notification, importantPeopleInfo:Seq[String]): String = {
| notification match {
| case Email(email, _, _) if importantPeopleInfo.contains(email) => s"You got an email from special someone!"
| case SMS(number, _) if importantPeopleInfo.contains(number) => s"You got an SMS from special someone!"
| case other => showNotification(other)
| }
| }
scala> val importantPeopleInfo = Seq("123-4567", "jenny@gmail.com")
scala> val someSms = SMS("123-4567", "Are you there?")
scala> val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
scala> val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!")
scala> val importantSms = SMS("123-4567", "I'm here! Where are you")
- 케이스 클래스 타입을 자동으로 확인한 패턴 매칭
scala> abstract class Device
scala> case class Phone(model: String) extends Device {
| def screenOff = "Turning screen off"
| }
scala> case class Computer(model: String) extends Device {
| def screenSaverOn = "Turning screen saver on..."
| }
// Device를 받아서 클래스 타입을 자동으로 확인
scala> def goIdle(device: Device) = device match {
| case p: Phone => p.screenOff
| case c: Computer => c.screenSaverOn
| }
scala> val phone = Phone("Galaxy")
scala> val computer = Computer("Macbook")
scala> println(goIdle(phone))
Turning screen off
scala> println(goIdle(computer))
Turning screen saver on...
scala> object PatternMatchingSample extends App {
| val VALID_GRADES = Set("A", "B", "C", "D", "F")
| def letterGrade(value: Any): String = value match {
| case x if (90 to 100).contains(x) => "A"
| case x if (80 to 90).contains(x) => "B"
| case x if (70 to 80).contains(x) => "C"
| case x if (60 to 70).contains(x) => "D"
| case x if (0 to 60).contains(x) => "F"
| case x: String if VALID_GRADES(x.toUpperCase()) => x.toUpperCase()
| }
| println(letterGrade(91))
| println(letterGrade(72))
| println(letterGrade(44))
| println(letterGrade("B"))
| }
믹스인 컴포지션
- 클래스와 트레잇을 상속할 때 서로 다른 부모의 변수, 메소드를 섞어서 새로운 정의 만드는 것
scala> abstract class A { val message: String }
scala> class B extends A { val message: "I'm an instance of class B" } // 추상 클래스 A를 상속하면서 변수 초기화
scala> trait C extends A { def loudMessage = message.toUpperCase() } // 추상 클래스 A를 상속하면서 함수 선언
scala> class D extends B with C // 믹스인하여 클래스 B의 message를 이용하는 loudMessage 함수 생성할 수 있음
scala> val d = new D
scala> println(d.message)
I'm an instance of class B
scala> println(d.loudMessage)
I'M AN INSTANCE OF CLASS B
Trait
- 자바의 인터페이스와 유사
- 메소드를 정의만 해놓을 수도 기본 구현을 할 수도 있지만, 생성자 파라미터는 가질 수 없음
- 가변/불변 변수 모두 선언 가능
- 트레잇을 구현하는 클래스에서 가변 변수는 수정 가능하지만 불변 변수는 수정 불가능
- 트레잇의 기본 메소드는 상속되고, override로 메소드 재정의
- extends로 상속하고 여러 개의 트레잇을 with로 동시 구현 가능
- 멤버변수를 가질 수는 없지만 여러 개의 트레잇 상속 가능
scala> trait Machine {
| val serialNumber: Int = 1
| def work(messgae: String)
| }
scala> trait krMachine {
| var countryCode: String = "kr"
| def print() = println("한글 출력")
| }
scala> class Computer(location: String) extends Machine with krMachine {
| this.countryCode = "us" // 값 변경
| def work(messgae: String) = println(message)
| }
scala> class Car(location: String) extends Machine with krMachine {
| def work(messgae: String) = println(message)
| override def print() = println("운전 중입니다.") // 메소드 재정의
| }
scala> var machine = new Computer("노트북")
scala> var car = new Car("포르쉐")
scala> machine.work("computing...")
computing...
scala> machine.print()
한글 출력
scala> println(machine.countryCode)
us
scala> car.print()
운전 중입니다.
scala> println(car.countryCode)
kr