Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Web
- hackthissite
- BOF
- deep learning
- mysql
- hackerschool
- flask
- Shellcode
- c
- Linux
- 백엔드
- 웹해킹
- BOF 원정대
- c++
- 러닝 스칼라
- 인공지능
- php
- webhacking
- 경제
- Scala
- 리눅스
- Javascript
- hacking
- 딥러닝
- 러닝스칼라
- 챗GPT
- 파이썬
- Python
- backend
- ChatGPT
Archives
- Today
- Total
jam 블로그
러닝스칼라 9장 - 객체, 케이스 클래스, 트레이트 본문
728x90
객체(object)
- 하나 이상의 인스턴스를 가질 수 없는 형태의 클래스, 싱글턴(Singletom)
- 클래스 이름으로 객체에 접근
- JVM에서 최초로 접근될 때 인스턴스화
- 자바에서는 클래스의 특정 필드와 메소드를 정적(static)으로 지정.(인스턴스 기반 필드/메소드와 섞여있음) 스칼라는 전역 필드/메소드와 인스턴스 기반 필드/메소드를 분리
- class 대신 object 키워드 사용, 클래스 매개변수를 취할 수 없음
object <identifier> [extends <identifier>] [{ fields, methods, and classes }]
scala> object Hello { println("in Hello"); def hi = "hi" }
defined object Hello
scala> println(Hello.hi)
in Hello
hi
scala> println(Hello.hi)
hi
순수 함수와 외부 입출력을 이용하는 함수에 어울림, 해당 함수들은 클래스의 필드와 거의 상관이 없기 때문에 클래스 메소드로 적합하지 않음
Apply 메소드와 동반 객체
- 동반 객체 : 클래스와 동일한 이름을 가지고, 동일 파일 내에 정의되는 객체. 서로의 private/protected 멤버에 접근 가능.
- 객체의 apply 메소드 : 객체의 이름으로 호출하는 메소드, 팩토리(factory)패턴을 위해 주로 사용. // List(1, 2, 3), Option(1)
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Multiplier(val x: Int) { def product(y: Int) = x * y }
object Multiplier { def apply(x: Int) = new Multiplier(x) }
// Exiting paste mode, now interpreting.
defined class Multiplier
defined object Multiplier
scala> val tripler = Multiplier(3)
tripler: Multiplier = Multiplier@5af28b27
scala> val result = tripler.product(13)
result: Int = 39
scala> :paste
// Entering paste mode (ctrl-D to finish)
object DBConnection {
private val db_url = "jdbc://localhost"
private val db_user = "franken"
private val db_pass = "berry"
def apply() = new DBConnection
}
class DBConnection {
private val props = Map(
"url" -> DBConnection.db_url,
"user" -> DBConnection.db_user,
"pass" -> DBConnection.db_pass
)
println(s"Created new connection for " + props("url"))
}
// Exiting paste mode, now interpreting.
defined object DBConnection
defined class DBConnection
scala> val conn = DBConnection()
Created new connection for jdbc://localhost
conn: DBConnection = DBConnection@4d27d9d
객체를 가지는 명령줄 어플리케이션
- 자바의 public static void main ~, 프로그램 진입점
- 파라미터로 문자열(Array[String]로 유일한 파라미터 받고 Unit을 반환하는)을 가진 main 메소드를 포함한 객체 필요. 컴파일 후 해당 객체 파일에 대하여 scala 명령어로 실행.
$ cat > Cat.scala
object Cat {
def main(args: Array[String]) {
for (arg <- args) {
println( io.Source.fromFile(arg).mkString )
}
}
}
$ scalac Cat.scala
$ scala Cat Date.scala
object Date {
def main(args: Array[String]) {
println(new java.util.Date)
}
}
- App trait를 상속해서 만들 수 있음
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}
object HelloWorld extends App {
println("Hello, world!")
}
케이스 클래스(case class)
- 자동으로 생성되는 메소드를 포함하는 클래스, 또한 자동으로 동반객체가 생성되는데 해당 객체도 자동으로 생성된 메소드를 가짐
- 자동으로 생성되는 모든 메소드들은 케이스 클래스의 매개변수 목록을 기반으로 생성
Name | 위치 | 설명 |
---|---|---|
apply | 객체 | 케이스 클래스를 인스턴스화하는 팩토리 메소드 |
unapply | 객체 | 인스턴스를 그 인스턴스의 필드들의 튜플로 추출하여 패턴 매칭에 케이스 클래스 인스턴스를 사용하 수 있도록 함 |
copy | 클래스 | 요청받은 변경사항이 반영된 인스턴스의 사본을 반환함. 매개변수느 현재 필드 값으로 설정된 기본값을 갖는 클래스의 필드임 |
equals | 클래스 | 다른 인스턴스의 모든 필드가 이 인스턴스의 모든 필드와 일치하면 true 반환. 연산자 ==로도 호출 가능 |
hashCode | 클래스 | 인스턴스의 필드들의 해시 코드를 반환함. 해시 기반의 컬렉션에 유용함 |
toString | 클래스 | 클래스명과 필드들을 String으로 전환함 |
- 클래스 정의 앞에 case 키워드 추가
- 클래스 매개변수를 값 필드로 자동으로 전환하여 사용(val 직접 붙일 필요 없음)
**case class <identifier> ([var] <identifier>: <type>[, ... ])
[extends <identifier>(<input parameters>)]
[{ fields and methods }]**
scala> case class Character(name: String, isThief: Boolean)
defined class Character
scala> val h = Character("Hadrian", true)
h: Character = Character(Hadrian,true)
scala> val r = h.copy(name = "Royce")
r: Character = Character(Royce,true)
scala> h == r
res0: Boolean = false
scala> h match {
| case Character(x, true) => s"$x is a thief"
| case Character(x, false) => s"$x is not a thief"
| }
res1: String = Hadrian is a thief
데이터 전송 객체를 표현할 때 사용. (필요한 메소드들 자동으로 생성해줘서 편의 제공)
트레이트(trait)
- 다중 상속을 가능케 하는 클래스 유형 중 하나, 여러 개의 trait를 확장(mixin)할 수 있음
- 인스턴스화 할 수 없고, 클래스 매개변수를 취할 수 없음 (타입 매개변수 사용은 가능)
- trait 키워드를 사용해서 정의함, with 키워드를 사용하여 두 개 이상의 trait을 확장할 수 있음
trait <identifier> [extends <identifier>] [{ fields, methods, and classes }]
scala> trait HtmlUtils {
| def removeMarkup(input: String) = {
| input
| .replaceAll("""</?\w[^>]*>""","")
| .replaceAll("<.*>","")
| }
| }
defined trait HtmlUtils
scala> trait SafeStringUtils {
|
| // Returns a trimmed version of the string wrapped in an Option,
| // or None if the trimmed string is empty.
| def trimToNone(s: String): Option[String] = {
| Option(s) map(_.trim) filterNot(_.isEmpty)
| }
| }
defined trait SafeStringUtils
scala> class Page(val s: String) extends SafeStringUtils with HtmlUtils {
| def asPlainText: String = {
| trimToNone(s) map removeMarkup getOrElse "n/a"
| }
| }
defined class Page
scala> new Page("<html><body><h1>Introduction</h1></body></html>").asPlainText
res3: String = Introduction
scala> new Page(" ").asPlainText
res4: String = n/a
scala> new Page(null).asPlainText
res5: String = n/a
- 여러 개의 trait을 확장한 경우, 상속될 클래스와 트레이트의 수평적인 리스트를 받아서 한 클래스가 다른 클래스를 확장하는 수직적 체인으로 재구성(선형화)
- 오른쪽에서 왼쪽 순으로 가장 가까운 부모부터 높은 부모로 배치된다. (class D extends A with B with C => class D extends C extends B extends A)
scala> trait Base { override def toString = "Base" }
defined trait Base
scala> class A extends Base { override def toString = "A->" + super.toString }
defined class A
scala> trait B extends Base { override def toString = "B->" + super.toString }
defined trait B
scala> trait C extends Base { override def toString = "C->" + super.toString }
defined trait C
scala> class D extends A with B with C { override def toString = "D->" +
super.toString }
defined class D
scala> new D()
res50: D = D->C->B->A->Base
부모 클래스의 행위를 재정의, 부가적 기능을 추가 (부모 클래스와 기능을 추가한 trait)
scala> class RGBColor(val color: Int) { def hex = f"$color%06X" }
defined class RGBColor
scala> val green = new RGBColor(255 << 8).hex
green: String = 00FF00
scala> trait Opaque extends RGBColor { override def hex = s"${super.hex}FF" }
defined trait Opaque
scala> trait Sheer extends RGBColor { override def hex = s"${super.hex}33" }
defined trait Sheer
scala> class Paint(color: Int) extends RGBColor(color) with Opaque
defined class Paint
scala> class Overlay(color: Int) extends RGBColor(color) with Sheer
defined class Overlay
scala> val red = new Paint(128 << 16).hex
red: String = 800000FF
scala> val blue = new Overlay(192).hex
blue: String = 0000C033
셀프 타입
- 트레이트가 클래스에 추가될 때 특정 타입 또는 그 서브타입과 함께 사용되어야 함을 강제
- 트레이트 정의의 중괄호를 연 다음 식별자, 요청받은 타입, =>를 포함한다
scala> class A { def hi = "hi" }
defined class A
scala> trait B { self: A =>
| override def toString = "B: " + hi
| }
defined trait B
scala> class C extends B
<console>:9: error: illegal inheritance;
self-type C does not conform to B's selftype B with A
class C extends B
^
scala> class C extends A with B
defined class C
scala> new C()
res1: C = B: hi
입력 매개변수가 필요한 클래스에 트레이트로 기능을 추가하는데 사용, 트레이트는 입력 매개변수를 지정하지 않고도 클래스를 확장할 수 있음
scala> class TestSuite(suiteName: String) { def start() {} }
defined class TestSuite
scala> trait RandomSeeded { self: TestSuite =>
| def randomStart() {
| util.Random.setSeed(System.currentTimeMillis)
| self.start()
| }
| }
defined trait RandomSeeded
scala> class IdSpec extends TestSuite("ID Tests") with RandomSeeded {
| def testId() { println(util.Random.nextInt != 1) }
| override def start() { testId() }
|
| println("Starting...")
| randomStart()
| }
defined class IdSpec
트레이트를 이용하여 인스턴스화
- 클래스가 인스턴스화될 때 클래스에 트레이트를 추가, 클래스가 의존하는 기능이 클래스 정의 시점에 추가되지 않다가 클래스가 인스턴스화 될 때 주입됨(종속성 주입)
- 트레이트가 클래스를 확장하는 식, 왼쪽부터 오른쪽으로의 선형화
- 하나 이상의 트레이트를 with 키워드를 사용해서 클래스에 추가(extends는 사용 불가, 트레이트를 확장하는 것이 아닌 트레이트의 의한 확장)
scala> class User(val name: String) {
| def suffix = ""
| override def toString = s"$name$suffix"
| }
defined class User
scala> trait Attorney { self: User => override def suffix = ", esq." }
defined trait Attorney
scala> trait Wizard { self: User => override def suffix = ", Wizard" }
defined trait Wizard
scala> trait Reverser { override def toString = super.toString.reverse }
defined trait Reverser
scala> val h = new User("Harry P") with Wizard
h: User with Wizard = Harry P, Wizard
scala> val g = new User("Ginny W") with Attorney
g: User with Attorney = Ginny W, esq.
scala> val l = new User("Luna L") with Wizard with Reverser
l: User with Wizard with Reverser = draziW ,L anuL
trait 사용처(Programming in Scala)
간결한 인터페이스를 확장해 풍부한 인터페이스 만들기
trait에 간결한 인터페이스 역할을 하는 추상 메소드를 정의하고 풍부한 인터페이스 역할을 할 여러 메소드를 추상메소드를 이용하는 방식으로 구현(메소드 구현 넣을 수 있음)
해당 trait을 mix in한 클래스에서 추상 메소드로 지정한 간결한 인터페이스만 구현해주면 풍부한 인터페이스까지 포함한 클래스 완성
trait Rectangular { def topLeft :Point def bottomRight :Point // 2개의 추상 메소드만 mix in한 클래스에서 구현해주면 아래 모든 메소드 이용 가능 def left = topLeft.x def right = bottomRight.x def width = right - left // 여러 기하 관련 메소드.. } // Rectangle, Component 클래스 등에서 Rectangular trait를 mix in해 직사각형 객체를 편리하게 쓸 수 있게 만들 수 있다(기하학적 속성을 조회하는 모든 메소드를 갖출 수 있음)
// Orderd trait : 하나의 비교 연산자만 작성하면 모든 비교 연산자 구현을 대신할 수 있음 class Rational(n :Int, d :Int) extends Ordered[Rational] { // 타입 파라미터 명시해야 // ... // 두 객체가 동일하면 0, 자신이 인자보다 작으면 음수, 크면 양수 반환 // 아래 메소드 구현만 채워주면, 해당 클래스는 <, >, <=, >= 연산 모두 제공할 수 있게 된다 def compare(that: Rational) = (this.number * that.denom) - (that.number * this.denom) // equals는 정의하지 않음. Ordered를 상속하더라도 직접 정의해야 함. }
쌓을 수 있는 변경 정의
super 호출을 동적으로 바인딩한다. (클래스에서는 super 호출을 정적으로 바인딩한다) trait을 이용해 원하는 기능을 스택처럼 쌓아 올릴 수 있다.
abstract class IntQueue { def get() :Int def put(x: Int) } // (슈퍼클래스를 지정 함으로써) IntQueue를 상속한 클래스에만 mix in할 수 있음 trait Doubling extends IntQueue { abstract override def put(x : Int) = super.put(2 * x) // 큐에 있는 모든 정수를 두 배 만들기 } // 컴파일러에게 의도적으로 super의 메소드를 호출했다는 사실을 알리기 위해 abstract override로 표시해야 함 // 즉 어떤 trait에 abstract override메소드가 있다면, 그 trait는 반드시 해당 메소드에 대한 구체적 구현을 제공하는 클래스에 mix in 해야함 trait Incrementing extends IntQueue { abstract override def put(x : Int) = super.put(x + 1) // 큐에 모든 정수에 1을 더한다 } trait Filtering extends IntQueue { abstract override def put(x : Int) = if (x >= 0) super.put(x) // 큐에 있는 음수를 걸러낸다 } import scala.collection.mutable.ArrayBuffer class BasicIntQueue extends IntQueue { private val buf = new ArrayBuffer[Int] def get() = buf.remove(0) def put(x:Int) = buf += x override def toString() = buf.toString } val queue = new BasicIntQueue with Incrementing with Filtering queue.put(-1); queue.put(0); queue.put(1) // -> 1, 2 val queue2 = new BasicIntQueue with Filtering with Incrementing queue2.put(-1); queue2.put(0); queue2.put(1) // -> 0, 1, 2
'IT Book Study > 러닝 스칼라' 카테고리의 다른 글
러닝스칼라 10장 - 고급 타입 특징 (0) | 2020.09.28 |
---|---|
러닝스칼라 8장 - 클래스 (0) | 2020.09.28 |
러닝스칼라 7장 - 그 외의 컬렉션 (0) | 2020.09.28 |
러닝스칼라 6장 - 보편적인 컬렉션 (0) | 2020.09.28 |
러닝스칼라 5장 - 일급 함수 (0) | 2020.09.28 |
Comments