Programming in Sala 책으로 스칼라 스터디하면서 정리했던 내용이다. 지금은 3판 번역본도 나왔지만, 약간 앞서서 스터디를 시작해서 2판으로 진행하고 있다.
10장
추상 클래스
- 추상 클래스 : 구현이 없는 추상 멤버가 있는 클래스
abstract
- 인스턴스를 만들 수 없음
- 추상 메소드 : 추상 클래스 내의 구현이 없는 메소드
- 구체 메소드 : 구현이 있는 메소드
abstract class Element {
def contents: Array[String]
}
파라미터 없는 메소드 정의
abstract class Element {
def contents: Array[String]
def height: Int = contents.length
def width: Int = if (height == 0) 0 else contents(0).length
}
- 파라미터 없는 메소드와 빈 괄호 메소드를 자유롭게 섞어쓸 수 있음
- 서로 오버라이드 가능
- 메소드 호출시 빈 괄호 생략 가능
- 함수 호출시 프로퍼티 접근 이상의 작업이 있따면 괄호를 사용하는 것이 좋음
클래스 확장
class ArrayElement(conts: Array[String]) extends Element {
def contents: Array[String] = conts
}
- extends 절이 없으면
scala.AnyRef
를 상속 - 상속 : 슈퍼클래스의 모든 멤버는 서브클래스의 멤버
- 비공개 멤버 제외
- 이름과 파라미터가 동일한 멤버 정의가 존재하면 제외 = 오버라이드(추상 멤버의 경우는 구현)
val ae = new ArrayElement(Array("hello","world"))
ae.width // 슈퍼클래스 멤버 사용
- 서브타입 : 슈퍼클래스의 값을 필요로 하는 곳이라면 어디나 서브클래스의 값 사용 가능
val e: Element = new ArrayElement(Array("hello")) // Element 대신 사용
- 구성(composition) :
ArrayElement
이진 클래스 코드에 배열을 가리킬 참조 필드가 들어감
메소드와 필드 오버라이드
- 필드와 메소드가 동일한 네임스페이스에 속함
- 파라미터 없는 메소드를 필드(val)로 오버라이드 가능
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}
- 동일한 이름의 필드와 메소드를 동시에 정의 불가
class WontCompile {
private var f = 0 // 필드와 메소드가 같은 이름이므로
def f = 1 // 컴파일할 수 없음
}
- 스칼라의 네임스페이스
- 값 : 필드, 메소드, 패키지, 싱글톤 객체
- 타입 : 클래스, 트레이트 이름
파라미터 필드
class ArrayElement (
val contents: Array[String] // 파라미터와 필드를 결합한 파라미터 필드(parametric field)
) extends Element
val
,var
사용 가능private
,protected
,override
수식자 사용 가능
슈퍼클래스의 생성자 호출
class LineElement(s: String) extends ArrayElement(Array(s)) {
override def width = s.length
override def height = 1
}
override
수식자
- 부모 클래스에 있는 구체적 멤버를 오버라이드하는 모든 멤버에
override
수식자를 붙여야 함 - 추상 멤버 구현시에는 생략 가능
- 우연한 오버라이드 방지
다형성(서브타입 다형성)
Element 타입 변수가 ArrayElement, LineElement 등의 객체 참조 가능
val e1: Element = new ArrayElement(Array("hello","world"))
val ae: ArrayElement = new LineElement("hello")
val e2: Element = ae
동적 바인딩
메소드는 실행 시점에 실제 그 객체가 어떤 타입인가를 따름
abstract class Element {
def demo() {
println("Element's implementation invoked")
}
}
class ArrayElement extends Element {
override def demo() {
println("ArrayElement's implementation invoked")
}
}
class LineElement extends Element {
override def demo() {
println("LineElement's implementation invoked")
}
}
class UniformElement extends Element
def invokeDemo(e: Element) {
e.demo()
}
invokeDemo(new ArrayElement) // ArrayElement's implementation invoked
invokeDemo(new LineElement) // LineElement's implementation invoked
invokeDemo(new UniformElement) // Element's implementation invoked
final
- 서브클래스가 특정 멤버를 오버라이드 못하게 함
class ArrayElement extends Element {
final override def demo() { // 메소드 오버라이드 불가
println("ArrayElement's implementation invoked")
}
}
final class ArrayElement extends Element { // 클래스 상속 불가
override def demo() {
println("ArrayElement's implementation invoked")
}
}
상속과 구성 사용
- 구성과 상속은 이미 존재하는 클래스를 이용해 새로운 클래스를 정의하는 방법
- 구성 : 코드 재사용
- 상속
- is-a 관계 : 'ArrayElement는 Element이다'
- 슈퍼클래스의 타입으로 서비클래스를 사용하는 경우
예제 구현
- 메소드 구현
def above(that: Element): Element =
new ArrayElement(this.contents ++ that.contents) // ++ 연산은 두 배열을 연결
def beside(that: Element): Element =
new ArrayElement(
for (
(line1, line2) <- this.contents zip that.contents // zip 연산은 순서쌍으로 이뤄진 하나의 배열로 변환
) yield line1 + line2
)
override def toString = contents mkString "\n"
- 팩토리 구현
object Element {
private class ArrayElement(
val contents: Array[String]
) extends Element
private class LineElement(s: String) extends Element {
val contents = Array(s)
override def width = s.length
override def height = 1
}
private class UniformElement(
ch: Char,
override val width: Int,
override val height: Int
) extends Element {
private val line = ch.toString * width
def contents = Array.fill(height)(line)
}
def elem(contents: Array[String]): Element =
new ArrayElement(contents)
def elem(chr: Char, width: Int, height: Int): Element =
new UniformElement(chr, width, height)
def elem(line: String): Element =
new LineElement(line)
}
import Element.elem
abstract class Element {
def contents: Array[String]
def width: Int =
if (height == 0) 0 else contents(0).length
def height: Int = contents.length
def above(that: Element): Element =
elem(this.contents ++ that.contents)
def beside(that: Element): Element =
elem(
for (
(line1, line2) <- this.contents zip that.contents // zip 연산은 순서쌍으로 이뤄진 하나의 배열로 변환
) yield line1 + line2
)
override def toString = contents mkString "\n"
}
- widen, heighten 메소드 추가
import Element.elem
abstract class Element {
def contents: Array[String]
def width: Int =
if (height == 0) 0 else contents(0).length
def height: Int = contents.length
def above(that: Element): Element = {
val this1 = this widen that.width
val that1 = that widen this.width
elem(this1.contents ++ that1.contents)
}
def beside(that: Element): Element = {
val this1 = this heighten that.height
val that1 = that heighten this.height
elem(
for (
(line1, line2) <- this1.contents zip that1.contents // zip 연산은 순서쌍으로 이뤄진 하나의 배열로 변환
) yield line1 + line2
)
}
def widen(w: Int): Element =
if (w <= width) this
else {
val left = elem(' ', (w - width) / 2, height)
val right = elem(' ', w - width - left.width, height)
left beside this beside right
}
def heighten(h: Int): Element =
if (h <= height) this
else {
val top = elem(' ', width, (h - height) / 2)
val bot = elem(' ', width, h - height - top.height)
top above this above bot
}
override def toString = contents mkString "\n"
}
- Spiral 애플리케이션
import Element.elem
object Spiral {
val space = elem(" ")
val corner = elem("+")
def spiral(nEdges: Int, direction: Int): Element = {
if (nEdges == 1)
elem("+")
else {
val sp = spiral(nEdges - 1, (direction + 3) % 4)
def verticalBar = elem('|', 1, sp.height)
def horizontalBar = elem('-', sp.width, 1)
if (direction == 0)
(corner beside horizontalBar) above (sp beside space)
else if (direction == 1)
(sp above space) beside (corner above verticalBar)
else if (direction == 2)
(space beside sp) above (horizontalBar beside corner)
else
(verticalBar above corner) beside (space above sp)
}
}
def main(args: Array[String]) {
val nSides = args(0).toInt
println(spiral(nSides, 0))
}
}
'Developing' 카테고리의 다른 글
Programming in Scala 스터디 정리 - 11장. 스칼라의 계층구조 (0) | 2017.09.17 |
---|---|
Programming in Scala 스터디 정리 - 9장. 흐름 제어 추상화 (0) | 2017.06.15 |
Programming in Scala 스터디 정리 - 8장. 함수와 클로저 (0) | 2017.06.14 |