10장 composition and inheritance - codeport/scala GitHub Wiki


kingori

10.1

10장 예제는 text layout framework임.

combinator: elements를 엮어 새로운 element 를 생성하는 opertaor

10.2

abstract type은 class 앞에 keyword abstract를 붙임

abstract class Element {}

java와 달리 abstract method 앞에 abstract keyword가 필요없음. body가 없다면 abstract method 임. cf. concrete method

scala> class A { def abc: Int }
<console>:7: error: class A needs to be abstract, since method abc is not defined
   class A { def abc: Int }
         ^
scala> abstract class A { def abc: Int }
defined class A   

definition vs. declaration : signature를 명시했다면 선언(declaration), 내용이 정의되었다면 정의(definition). abstract method는 선언만 있다고 정의는 없는 경우.

10.3

  • parameterless method: () 도 명시하지 않은 method.
    • 매개변수를 갖지 않으며, 객체의 상태를 변경하지 않는 메서드의 경우에 권장하는 convention.
    • uniform access principle을 지원: 속성을 필드로 구현하건 메서드로 구현하건 클라이언트 코드는 영향을 받지 않아야 함. -> 즉, 마치 field처럼 보이는 method라는 것인가? java였다면 method라면 () 를 붙이고, 아니라면 그냥 썼어야.
  • empty-paren method: () 를 명시한 method

parameterless method 메서드를 호출할 때 뒤에 () 를 붙이니 오류남. 그래서 uniform access principle을 지원한다는 것인가 봄.

class A  { def height: Int = "123".length
           def height2(): Int = "123".length
           val height3: Int = "123".length 
 }

scala> val b =new A
b: A = A@1532075
scala> b.height2
res39: Int = 3
scala> b.height2()
res40: Int = 3
scala> b.height3
res41: Int = 3
scala> b.height3()
<console>:10: error: Int does not take parameters
              b.height3()
                       ^
scala> b.height
res44: Int = 3
scala> b.height()
<console>:10: error: Int does not take parameters
              b.height()
                      ^

parameterless method와 field는 사용하는 방법은 동일하지만, 실행속도 측면에선 차이가 있음. 당연히 method는 매 호출마다 evaluation이 되어야 하니. 대신 field는 메모리를 차지함.

parameterless method로 정의되어 있지 않은 메서드라도 argument가 없다면 () 를 생략할 수 있으므로 uniform access principle을 준수할 수 있음. ex) new String("aaa").length

반대로, 인자가 없는 method라 하더라도 side-effect를 동반한다면 가급적 () 를 붙이길 권장(convention 수준).

"abc".length // no side-effect. drop ()
println() // has side-effect. specify ()

###10.4

클래스 확장 시 extends. java와 동일. scala의 AnyRef는 java의 Object에 해당.

상속

  • 상위클래스의 모든 멤버를 상속하지만

  • private 은 상속되지 않으며

  • 같은 이름, 매개변수를 가진 멤버는 상속되지 않음 (overriding & implementation)

  • subtyping: superclass 위치엔 subclass의 값이 들어갈 수 있음

10.5

java와 달리 scala의 field와 method는 동일한 namespace에 위치함. 따라서 parameterless method를 field로 overriding할 수 있음(헉!) 또한 method와 field가 같은 이름을 가질 수 없음.

namespace

  • java: field, method, type, package
  • scala: values(field, method, package, singleton object) , type (class, trait)
    • singleton object의 이름과 class 이름은 서로 다른 namespace를 가지므로 서로 관련이 없나보군!

10.6

parametric field: combine parameter and field. 생성자에 val/var 명시하는 내용. private, protected, override를 parameteric field에 붙일 수 있음.

10.7

superclass의 생성자 호출 시 extends class명 뒤에 param을 넘기면 됨

class B(s: String, ... ) extends A(Array(s)) {... }

10.8

부모 class의 concrete member를 override할 땐 override를 반드시 명기해야 함. 단, abstract method를 구현할 경우엔 optional.

scala> abstract class A1 { def a:Int ; def b:Int = 4 }
defined class A1

scala> class A2 extends A1 { def a:Int = 3; def b:Int = 5 }
<console>:8: error: overriding method b in class A1 of type => Int;
 method b needs `override' modifier
       class A2 extends A1 { def a:Int = 3; def b:Int = 5 }
                                                ^

scala> class A2 extends A1 { def a:Int = 3; override def b:Int = 5 }
defined class A2

override를 강제함으로써 fragile base class 문제를 일으키는 accidental overrides 현상을 방지할 수 있음.

10.9

polymorphism에서 메서드 호출과 표현식은 동적 바인딩 됨. 즉, runtime에 어떤 클래스의 기능이 호출될 지 결정됨.

10.10

java와 마찬가지로 override를 막기 위해선 member에 final을 명시함. class, method에 명시할 수 있음.

10.11

fragile base class 문제를 겪지 않으려면 inheritance보다는 composition을 사용하자.

10.12

scala의 array는 scala.Seq 의 인스턴스로 변환됨. 이 클래스는 sequence-like 자료구조를 표현함.

for 문 안에서 tuple을 다룰 때

for( (line1, line2) <- Array(1,2,3) zip Array("a","b") ) yield line1 + line2

10.13

factory method를 만드는 일반적인 방법: companion object를 이용.

10.14, 15, 16

skip

⚠️ **GitHub.com Fallback** ⚠️