-
Notifications
You must be signed in to change notification settings - Fork 0
10장 composition and inheritance
kingori
10장 예제는 text layout framework임.
combinator: elements를 엮어 새로운 element 를 생성하는 opertaor
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는 선언만 있다고 정의는 없는 경우.
- 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의 값이 들어갈 수 있음
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를 가지므로 서로 관련이 없나보군!
parametric field: combine parameter and field. 생성자에 val/var 명시하는 내용. private, protected, override를 parameteric field에 붙일 수 있음.
superclass의 생성자 호출 시 extends class명 뒤에 param을 넘기면 됨
class B(s: String, ... ) extends A(Array(s)) {... }
부모 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 현상을 방지할 수 있음.
polymorphism에서 메서드 호출과 표현식은 동적 바인딩 됨. 즉, runtime에 어떤 클래스의 기능이 호출될 지 결정됨.
java와 마찬가지로 override를 막기 위해선 member에 final을 명시함. class, method에 명시할 수 있음.
fragile base class 문제를 겪지 않으려면 inheritance보다는 composition을 사용하자.
scala의 array는 scala.Seq 의 인스턴스로 변환됨. 이 클래스는 sequence-like 자료구조를 표현함.
for 문 안에서 tuple을 다룰 때
for( (line1, line2) <- Array(1,2,3) zip Array("a","b") ) yield line1 + line2
factory method를 만드는 일반적인 방법: companion object를 이용.
skip