독서관련/Programming in Scala

Scala CH10. 상속과 구성

ColinKang 2020. 3. 29. 13:17
아래의 글은 마틴 오더스키,렉스 스푼,빌 베너스 공저 / 오현석,이동욱,반영록 공역, 『Programming in Scala 3/e』,에이콘출판사(2017), CH01의 내용을 기반으로 작성하였습니다.

콤비네이터

 

메소드의 경우 구현이 없으면 추상 메소드다

 

클래스는 추상 메소드를 선언한다 ( not 정의)

 

파라메터 없는 메소드

빈 괄호나 아에 괄호가 없는 메소드

필드나 메소드 중 어떤 방식의로 속성을 정의하더라도 클라이언트 코드에는 영향을 끼치지 말아야 한다는 원칙 => 단일 접근 원칙

 

abstract class Element {

 

 

    def contents: Array[String]

    def height: Int = contents.length

    def width: Int = if (height == 0) 0 else contents(0).length

 

 

 

 

}

 

 

abstract class Element1 {

    def contents : Array[String]

    val height = contents.length

    var width = if (height == 0) 0 else contents(0).length

}

 

 

-- 필드로 할 경우 클래스 초기화 시 값이 미리 계산됨.

 

스칼라에서는 파라메터 없는 메소드와 빈 괄호 메소드를 자유롭게 섞어 쓸 수 있게 한다.

메소드 오보라이드 용이

호출하는 함수가 어떠한 작업을 수행한다면 빈 괄호를 사용하라

프로퍼티에 대한 접근만 수행한다면 괄호를 생략해라

 

- 클래스 확장

비공개가 아닌 부모의 멤버를 모두 물려받는다

서브타입으로 만든다

서브 클래스 라고 한다

부모는 수퍼 클래스

생략하면 scala.AnyRef 상속함.

 

오버라이드 적용됨.

 

자바에 4개의 네임 스페이스 : 필드, 메소드, 타입, 패키지

스칼라에 2개의 네임 스페이스 : 값 (필드, 메소드, 패키지, 싱글톤 객체), 타입 (클래스와 트레이트 이름)

스칼라가 필드와 메소드를 동일한 네임스페이스로 취급하는 우이유는 정확히 파라미터 없는 메소드를 val로 오버라이드 하기 위해서 이다

 

- 파라메터 필드

class ArrayElement (

    val contents: Array[String]

    // 동일한 필드와 파라메터를 동시에 정의하는 단축표기

) extends Element

 

 

class Cat {

    var dangerous = false

}

 

 

class Tiger (

    override val dangerous: Boolean,

    private val age: Int

) extends Cat

 

- 슈퍼 클래스의 생성자 호출

class LineElement(s: String) extends ArrayElement(Array(s)) {

    override def width = s.length

    override def height = 1

}

 

 

// override def hight = 1

// 컴파일 오류

 

추상 멤버를 구현한 경우에는 override 생략 가능

우연한 오버라이드는 깨지기 쉬운 기반 클래스 라고 불리는 문제의 가장 흔한 사례임.

클래스 계층에서 기반 클래스 (슈퍼 클래스) 에 추가한 멤버로 인해 클라이언트 코드가 깨지는 위험

상위에서 정의 되어 있는 것을 하위에서 override 없이 적기만 해도 컴파일 오류 남. 즉 사전에 오작동 방지 상위 메소드에서 추가하는 경우

 

- 다형성과 동적 바인딩

Element 타입의 변수가 하위 ArrayElement 타입의 객체를 참고 가능 : 서브 타입 다형성

 

val e: Element = new ArrayElement(Array("hello", "world"))

 

 

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)

}

 

변수나 표현식에 대한 메소드 호출을 동적으로 바인딩

upcating 되더라도 실제 ref class의 오버라이딩 메소드가 호출됨.

 

- final 멤버 선언

override 못하게 선언

class와 member에 적용 가능

 

- 상속과 구성 사용

상속 is a 관계

 

- above, beside. toString 구현

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 =

        new ArrayElement(this.contents ++ that.contents)

 

 

    def beside(that: Element): Element =

        new ArrayElement(

            for (

                (line1, line2) <- this.contents zip that.contents

            ) 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 = {

        val this1 = this widen that.widen

        val that2 = that widen this.width

        elem(this.contents ++ that.contents)

    }

 

    def beside(that: Element): Element = {

        val this1 = this heighten that.height

        val that1 = that heighten this.height

        elem(

            for (

                (line1, line2) <- this.contents zip that.contents

            ) yield line1 + line2

        )

    }

     

    def widen(w: Int): Element =

        if (w <= width) this

        else {

            val left = elem(' ', (w - width) / 2, height)

            var right = elem(' ', w - width - left.width, height)

            left beside this beside right

        }

 

 

    def heighten(h: Int): Element =

        if (h <= height) this

        else {

            var top = elem(' ', width, (h - height) / 2

            var bot = elem(' ' , width, h - height - top.height)

            top above this above bot

        }

 

    override def toString = contents mkString "\n"

}