02. class

암모나이트 REPL

  • 기본 repl보다 좋음
  • 스칼라 스크립트를 지원
brew install ammonite-repl

Class 정의하기


In [1]:
class Person {
    val firstName = "Martin"
    val lastName = "Odersky"
    def name = firstName + " " + lastName    
}


Out[1]:
defined class Person

In [1]:
Person


cmd1.sc:1: not found: value Person
val res1 = Person
           ^
Compilation Failed

In [1]:
// Object는 바로 생성되나, Class의 경우 바로 생성되진 않음

In [2]:
// new라는 키워드를 사용
val martin = new Person


Out[2]:
martin: Person = $sess.cmd0Wrapper$Helper$Person@37dc5ef7

In [7]:
val martin2 = new Person


Out[7]:
martin2: Person = $sess.cmd0Wrapper$Helper$Person@7ebec361

In [3]:
martin.firstName


Out[3]:
res2: String = "Martin"

In [4]:
martin.lastName


Out[4]:
res3: String = "Odersky"

In [3]:
object greeter {
    def greet(p: Person) =
    "Greeting, " + p.firstName + " " + p.lastName    
}


Out[3]:
defined object greeter

In [4]:
greeter.greet(martin)


Out[4]:
res3: String = "Greeting, Martin Odersky"

In [8]:
greeter.greet(martin2)


Out[8]:
res7: String = "Greeting, Martin Odersky"

In [8]:
greeter.greet("Martin")


cmd8.sc:1: type mismatch;
 found   : String("Martin")
 required: cmd8Wrapper.this.cmd4.cmd0.wrapper.Person
val res8 = greeter.greet("Martin")
                         ^
Compilation Failed

In [9]:
greeter greet martin


Out[9]:
res8: String = "Greeting, Martin Odersky"

기본 생성자 ( Constructor )


In [10]:
class Person(first: String, last: String) {
    val firstName = first
    val lastName = last
    def name = firstName + " " + lastName
}


Out[10]:
defined class Person

In [11]:
val heungin = new Person("Heunjin", "Kim")


Out[11]:
heungin: Person = $sess.cmd9Wrapper$Helper$Person@3d5be80f

In [12]:
heungin.name


Out[12]:
res11: String = "Heunjin Kim"

같은 이름의 클래스를 생성할 경우, Repl에선 replace하나 컴파일할 경우엔 error 발생

class 선언시 val이라고 명시하면 바로 필드가 됩니다


In [13]:
class Person(val firstName: String, val lastName : String){
    def name = firstName + " " + lastName
}


Out[13]:
defined class Person

In [14]:
val heungin = new Person("Heunjin", "Kim")


Out[14]:
heungin: Person = $sess.cmd12Wrapper$Helper$Person@77853e65

In [15]:
new Person("Heungjin", "Kim").name
// 생성되고 바로 method 호출


Out[15]:
res14: String = "Heungjin Kim"

In [16]:
new Person("Heungjin", "Kim").name
// 위와 다른 인스턴스


Out[16]:
res15: String = "Heungjin Kim"

In [17]:
res14 == res15
// repl이면 false로!


Out[17]:
res16: Boolean = true

In [18]:
res16 == res14


Out[18]:
res17: Boolean = false

keyword parameter & default parameter values


In [19]:
class Person(val firstName: String, val lastName: String = "!"){
    def name = firstName + " " + lastName
}


Out[19]:
defined class Person

default parameter는 모두 뒤에 붙어야 함! 그래야 하나 넣을 때 앞부터 쇽쇽!


In [20]:
new Person(lastName = "Last", firstName="First")


Out[20]:
res19: Person = $sess.cmd18Wrapper$Helper$Person@63aef9fa

In [21]:
new Person("seongyun")


Out[21]:
res20: Person = $sess.cmd18Wrapper$Helper$Person@3af689b2

class Apply


In [5]:
class Adder(amount: Int) {
    def apply(in: Int): Int = in + amount
}


Out[5]:
defined class Adder

In [6]:
val add3 = new Adder(3)


Out[6]:
add3: Adder = $sess.cmd4Wrapper$Helper$Adder@5bc432b1

In [8]:
new Adder(3)(2)


Out[8]:
res7: Int = 5

In [24]:
add3(2)


Out[24]:
res23: Int = 5

In [32]:
class Avengers(val realName: String, val position: String)


Out[32]:
defined class Avengers

In [27]:
val CaptainAmerica = new Avengers("Steve Rogers", "Leader")


Out[27]:
CaptainAmerica: Avengers = $sess.cmd25Wrapper$Helper$Avengers@21c33d1e

In [56]:
val CaptainAmerica = new Avengers("Steve Rogers", "Leader")


Out[56]:
CaptainAmerica: Avengers = $sess.cmd35Wrapper$Helper$Avengers@54ce7dbe

In [57]:
val IronMan = new Avengers("Tony Stark", "Range Deler")


Out[57]:
IronMan: Avengers = $sess.cmd35Wrapper$Helper$Avengers@7d059173

In [58]:
val Hulk = new Avengers("Bruce Banner", "Tanker")


Out[58]:
Hulk: Avengers = $sess.cmd35Wrapper$Helper$Avengers@2ffb7453

In [59]:
val Thor = new Avengers("Thor Odinson", "God")


Out[59]:
Thor: Avengers = $sess.cmd35Wrapper$Helper$Avengers@7386aca7

In [60]:
object GodFollower {
    def inspect(avengers: Avengers): String = ""
}


Out[60]:
defined object GodFollower

In [72]:
object GodFollower {
  def inspect(avengers: Avengers): String = if (avengers.position.toLowerCase == "god") "Oh My God!" else "Mere mortal"
}


Out[72]:
defined object GodFollower

In [73]:
println(GodFollower.inspect(Thor) == "Oh My God!")
println(GodFollower.inspect(CaptainAmerica) == "Mere mortal")


true
true

In [67]:
GodFollower.inspect(CaptainAmerica)


Out[67]:
res66: String = "Mere mortal"

Scala Type

  • Nothing은 모든 타입의 하위 타입
  • Null은 List와 Option과 Class의 하위 타입

Nothing, Null


In [74]:
def badness = throw new Exception("Error")
// badness : Nothing
// 모든 것의 하위 타입


Out[74]:
defined function badness

In [ ]:
null
// Null=null

In [76]:
var bar = if(true) 123 else badness


Out[76]:
bar: Int = 123

In [77]:
val baz = if(false) "it worked" else null
// Baz: String = null


Out[77]:
baz: String = null

Companion Objects


In [78]:
class Timestamp(val seconds: Long)


Out[78]:
defined class Timestamp

In [79]:
// 자바는 static method가 있어서 클래스의 인스턴스를 생성하지 않아도 되는 것을 제공해주는데 그런 것들을 companion objects로 지정
object Timestamp {
    def apply(hours: Int, minutes: Int, seconds: Int): Timestamp = 
        new Timestamp(hours*60*60 + minutes*60 + seconds)
}
// 메소드로 생성되게 해줬던 것임!!


Out[79]:
defined object Timestamp

In [80]:
Timestamp(1, 1, 1).seconds


Out[80]:
res79: Long = 3661L

In [81]:
val t = Timestamp(1, 1, 1)


Out[81]:
t: Timestamp = $sess.cmd77Wrapper$Helper$Timestamp@69e81af8

In [83]:
t.seconds


Out[83]:
res82: Long = 3661L

In [84]:
val t2 = new Timestamp(3662)


Out[84]:
t2: Timestamp = $sess.cmd77Wrapper$Helper$Timestamp@69111a3a

In [85]:
t2.seconds


Out[85]:
res84: Long = 3662L

Case classes

  • 기본적으로 불변
  • 패턴 매칭을 통해 분해가능
  • 레퍼런스가 아닌 구조적인 동등성으로 비교됨
  • 초기화와 운영이 간결함

--

  • 특징 1. Field for each constructor argument
  • 특징 2. Default Tostring Method
  • 특징 3. Default equals, hashcode method
  • 특징 4. Copy Method를 지원

In [86]:
case class Person(firstName: String, lastName: String) {
  def name = firstName + " " + lastName
}


Out[86]:
defined class Person

In [87]:
val martin = new Person("Martin", "Odersky") // we have a class


Out[87]:
martin: Person = Person("Martin", "Odersky")

In [88]:
martin.firstName


Out[88]:
res87: String = "Martin"

In [89]:
val martin2 = new Person("Martin", "Odersky")


Out[89]:
martin2: Person = Person("Martin", "Odersky")

In [91]:
martin == martin2
// case를 사용하면 값만 같아도 true! classㄹㄹ 사용할 경우엔 false
// 데이터를 표현할 때 주로 사용


Out[91]:
res90: Boolean = true

특징 1. Field for each constructor argument


In [92]:
martin.name


Out[92]:
res91: String = "Martin Odersky"

특징 2. Default Tostring Method


In [93]:
martin


Out[93]:
res92: Person = Person("Martin", "Odersky")

In [93]:
// 기존엔 생성할 때 Person@63aef9fa 이런식이였는데 더 깔끔하게 보일 수 있음

특징 3. Default equals, hashcode method

  • case로 생성되면 인자가 같으면 같다

In [94]:
martin equals martin2


Out[94]:
res93: Boolean = true

특징 4. Copy Method를 지원


In [95]:
martin.copy()


Out[95]:
res94: Person = Person("Martin", "Odersky")

In [96]:
martin.copy(firstName = "Martin2")
// firstName을 바꿔서 만든 Case Class


Out[96]:
res95: Person = Person("Martin2", "Odersky")
  • 함수형 프로그래밍에선 보통 불변의 인스턴스를 가지고 있음. 이건 Spark의 RDD 개념과 동일
  • 분산 처리할 땐 불변이 편해서 이렇게 사용! 가변적이면 다 체크를 해야하니깐!!!

참고 : Case objects


In [97]:
case object Citizen {
    def firstName = "Martin"
    def lastName = "Odersky"
    def name = firstName + ' ' + lastName
}
// 다음 시간에 설명드리겠습니다!


Out[97]:
defined object Citizen

Pattern Matching


In [98]:
case class Person(firstName: String, lastName: String)


Out[98]:
defined class Person

In [99]:
object Stormtrooper {
    def inspect(person: Person): String = person match {
        case Person("Luke", "Skywalker") => "Stop, rebel scum!"
        case Person("Han", "Solo") => "Stop, rebel scum!"
        case Person(first, last) => s"Move along, $first"
    }
}


Out[99]:
defined object Stormtrooper

In [101]:
Stormtrooper.inspect(Person("Martin", "Odersky"))


Out[101]:
res100: String = "Move along, Martin"

In [102]:
Stormtrooper.inspect(Person("Han", "Solo"))


Out[102]:
res101: String = "Stop, rebel scum!"

Scala String Interpolation


In [103]:
val first = "test"
s"Move along, $first" // first 변수가 들어감


Out[103]:
first: String = "test"
res102_1: String = "Move along, test"

In [104]:
s"Hello ${1+1}"


Out[104]:
res103: String = "Hello 2"
  • 이외에도 f를 붙이면 formating!

Pattern Syntax(1/4) : Name


In [106]:
Person("first","last")


Out[106]:
res105: Person = Person("first", "last")

In [107]:
val last = "last"


Out[107]:
last: String = "last"

In [108]:
Person(first, last)


Out[108]:
res107: Person = Person("test", "last")

Pattern Syntax(2/4) : Under Score


In [108]:
Person(first, _)


cmd108.sc:1: missing parameter type for expanded function ((x$1) => Person(first, x$1))
val res108 = Person(first, _)
                           ^
Compilation Failed

Pattern Syntax(3/4) : Literal


In [109]:
Person("Luke", "Skywalker")


Out[109]:
res108: Person = Person("Luke", "Skywalker")

실습 - 위에서 했던 것을 패턴 매칭으로


In [ ]:
case class Avengers(realName: String, position: String)

val CaptainAmerica = Avengers("Steve Rogers", "Leader")
val IronMan = Avengers("Tony Stark", "Range Deler")
val Hulk = Avengers("Bruce Banner", "Tanker")
val Thor = Avengers("Thor Odinson", "God")

In [117]:
object GodFollower {
  def inspect(avengers: Avengers): String = avengers match {
      case Avengers(_, "God") => "Oh My God!"
      case _ => "Mere mortal"
  }
}


Out[117]:
defined object GodFollower

In [118]:
println(GodFollower.inspect(Thor) == "Oh My God!")
println(GodFollower.inspect(CaptainAmerica) == "Mere mortal")


true
true

In [120]:
case class Music(title: String, author: Author)
case class Author(firstName: String, secondName: String)


Out[120]:
defined class Music
defined class Author

In [136]:
object MusicPlayer {
    // Author의 secondName이 Mozart인 경우와 아닌 경우
    def play(music: Music): String = music match {
        case Music(title, Author(_, "Mozart")) => s"$title by Mozart"
        case Music(t, Author(f, s)) => s"$t by $f $s"
    }
}


Out[136]:
defined object MusicPlayer

In [137]:
val music1 = Music("애국가", Author("익태", "안"))
val music2 = Music("Requiem", Author("Wolfgang Amodeus", "Mozart"))


Out[137]:
music1: Music = Music(애국가,Author(익태,안))
music2: Music = Music(Requiem,Author(Wolfgang Amodeus,Mozart))

In [138]:
println(MusicPlayer.play(music1) ==  "애국가 by 익태 안")
println(MusicPlayer.play(music2) ==  "Requiem by Mozart")


true
true

Trait

  • 여러 클래스가 같은 속성을 공유하고 있을 때, 사용하는 클래스
  • 공식 문서

In [139]:
trait Similarity {
  def isSimilar(x: Any): Boolean
  def isNotSimilar(x: Any): Boolean = !isSimilar(x)
}


Out[139]:
defined trait Similarity

SEALED TRAIT

  • 봉인된 Trait
  • 같은 file에서만 extend되서 사용할 수 있고, 다른 곳에선 다루지 못함

In [140]:
sealed trait Symbol
case class Note(name: String, duration: String, octave: Int) extends Symbol
case class Rest(duration: String) extends Symbol


Out[140]:
defined trait Symbol
defined class Note
defined class Rest

스칼라

  • input 정의한 후, output 정의
  • 그 이후 내부 로직 생성

In [ ]: