[12강] 상속

2015. 4. 25. 00:00Programming/Scala

스칼라에서도 추상 클래스가 존재하며 그에 대한 상속도 존재한다.


abstract class Element{

    def contents: Array[String]

}


위의 예제는 Element 라는 추상 클래스를 정의한 것이고, contents라는 매서드를 정의만 한 것이다.

contents라는 매서드가 정의만 되었기 때문에 추상 맴버가 되고 (추상 맴버일 경우에는 맴버 앞에 abstract를 붙여주지 않아도 됨) 추상 맴버를 가진 class는 자연스럽게 추상 클래스가 된다. (class 앞에 abstract 붙여 줘야 한다.)

이 Element를 상속 받기 위해서는 contents라는 매서드를 반드시 구현을 해주어야 한다.


상속은 자바와 마찬가지로 extends 키워드를 사용한다. 상속은 private 맴버를 제외한 모든 맴버를 상속 받는다.


class ArrayElement(conts: Array[String]) extends Element{

   def contents: Array[String] = cont

}


Element 클래스를 부모로 하는 ArrayElement라는 클래스를 정의하였고, contents 매서드의 내용도 구현하였다.


스칼라에서는 매서드와 필드가 같은 네임스페이스에 속하기 때문에 파라미터가 없는 매서드의 경우에는 필드로 변경할 수가 있다.


class ArrayElement(conts:Array[String]) extends Element{

    val contents: Array[String] = cont

}


부모 클래스에는 분명 def로 정의되어 있지만 자식 클래스에서 val로 변경하여도 컴파일에는 전혀 문제되지 않는다. 물론 동작에도 전혀 문제가 없다.

이런 동작 방식 때문에 스칼라에서는 동일한 이름을 가진 매서드와 필드를 함께 사용하지 못한다.


class Test(n:Int){

    def aaa = 1

    val aaa = 0

}                                // 안됨!!


위에서 contents라는 매서드를 필드로 정의했기 때문에 아래와 같이 생성자 함수에 직접 값을 집어넣는 것도 가능하다.


class ArrayElement(val contents: Array[String]) extends Element


상속이 가능하기 때문에 물론 override도 가능하다. 부모 클래스에 정의되어 있는 기능을 override 하고 싶을 경우에는 아래와 같이 타입 앞에 override란 키워드를 써주면 된다.


override val aaa = 0


이번에는 ArrayElement를 상속하는 LineElement를 정의했다고 가정하자.


class LineElement(s: String) extends ArrayElement{

}


하지만 보다시피 ArrayElement 를 호출하기 위해서는 Array[String] 타입의 파라미터가 필요하다. 

이럴 경우에는 어떻게 할까??


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

}


위와 같이 상속 받는 클래스 이름 뒤에 괄호를 붙인 후 매개변수를 넘기면 부모 클래스의 생성자가 호출이 된다.


animal이란 추상클래스가 있고, dog와 cat, man이라는 자식 클래스가 있다고 가정하자.


abstract class Animal{

    def sound:String = "왁왁"

}


class Dog extends Animal{

    override def sound:String = "멍멍"

}


class Cat extends Animal{

    override def sound:String = 야옹"

}


class Man extends Animal


이제 여기서 Animal dog = new Dog() 라고 한 후 dog.sound와 Animal cat = new Cat(), Animal man = new Man()이라고 한 후 cat.sound, man.sound 를 각각 실행시키면 어떻게 될까?

정답은 각각 "멍멍"과 "야옹", "왁왁"이다. 


아무리 Animal 객체로 받았다고 하더라도 실행시에는 해당 타입을 동적으로 바인딩 하기 때문에 위와 같은 결과가 나타나게 된다.

위에서는 Dog와 Cat, Man으로 각각 동적 바인딩이 되었다.


해당 매서드에 대해 자식 클래스가 override 하는 것을 막고 싶을 때는 어떻게 해야 할까?

부모 클래스에서 해당 매서드 앞에 final 키워드를 붙여주면 된다.

클래스 앞에 final을 붙이면 해당 클래스에 대한 상속을 막을 수 있다.


final class ArrayElement extends Element

class StringElement extends ArrayElement    // Error


마지막으로 아까 만들었던 Element 클래스를 factory pattern을 써서 좀 더 리펙토링 해보자.


import Element.elem


abstract class Element{ 

    def contents: Array[String]

    def above(that: Element): Element = 

        elem(this.contents ++ that.contents)

}


object Element{

    private class ArrayElement(val contents: Array[String]) extends Element

    private class LineElement(s: String) extends Element {

        val contents = Array(s)

    }

    

    def elem(contents: Array[String]): Element = new ArrayElement(contents)


    def elem(line: String): Element = new LineElement(line)

}