Skip to content
kingori edited this page Oct 16, 2011 · 15 revisions

12.0

12.1 How traits work

kingori

  • trait 선언엔 keyword trait 사용.
  • class가 trait를 mix-in할 땐 extends(superclass가 없는 경우), with(superclass를 가진 경우)
    • 여러 trait를 mix-in할 경우엔 with A with B with C ...
class A {}
class B extends A with T1 with T2 { }   
  • trait도 type이다.
scala> trait TTT { }
defined trait TTT

scala> class CCC extends TTT { }
defined class CCC

scala> val a:TTT = new CCC()
a: TTT = CCC@17e5fde
  • trait의 method를 override할 수 있다
  trait A { def a() = 3 }
  class B extends A { override def a() = 5 }
  • java의 interface와달리 field, state를 가질 수 있다.
    • class parameter는 가질 수 없음 : java로 따지면 인자 없는 생성자만 가능함
    • class의 super는 statically bound, trait의 super는 dynamically bound

12.2 Thin versus rich interfaces

kingori

thin interface(method수가 적은 interface) vs. rich interface(method수가 많은 interface)

  • rich i/f는 다수의 method를 제공하므로 사용하는 측이 편함
  • thin i/f는 구현할 메서드 수가 적어 구현이 편함 ==> java는 thin i/f를 선호하는 경향이 있음. (많은 method를 구현하기 힘드니깐) 반면 scala의 trait를 이용하면 rich i/f 사용하기도 용이함.
    • 직접 구현해야 하는 abstract method와(thin i/f)와 함께 이 메서드들을 사용하는 다양한 method를 제공 (rich i/f)

12.3 Example: Rectangular objects

###kingori

skip

12.4 The Ordered trait

###kingori

trait Ordered : >, <, <=, >= 를 제공하는 trait. compare() 를 구현해야 함.

  • 주의: equals 는 알아서 구현해야!
class A(val n: Int) extends Ordered[A] {  def compare(that: A) = this.n - that.n; 
override def equals(that: Any):Boolean = { that match { case thatA: A => this.n == thatA.n; case _ => false } } }

12.5 Traits as stackable modifications

kingori

  • trait의 주요 용도
    • turning a thin interface into a rich one
    • providing stackable modifications to classes
  • stackable modification - method를 abstract override로 선언
trait Doubling extends IntQueue { // 이 trait를 extends하려는 class는 IntQueue를 extends해야 함
 abstract override def put(x: Int) { super.put(2 * x ) } // 남들이 먼저 구현을 제공하면 그 다음에 (마지막에) 호출됨
}    
  • mixin 선언 순서는 중요! : 순서는 다음 절에 나오지만 대충 앞에 나오는 mixin 부터 적용됨
abstract class Value { def value():Int  }
class ValueImpl(vall:Int) extends Value { override def value():Int = { println("valueimpl");vall } }
trait Add extends Value {  abstract override def value():Int = { println("add");super.value() + 3} }
trait Mult extends Value {  abstract override def value():Int = { println("mult");super.value() *2} }

scala> val a = new ValueImpl(3) with Add with Mult
a: ValueImpl with Add with Mult = $anon$1@e904c4

scala> a.value //  ( 3 + 3 ) * 2  : a -> Mult -> Add -> ValueImpl
mult
add
valueimpl
res90: Int = 12

scala> val b = new ValueImpl(3) with Mult with Add
b: ValueImpl with Mult with Add = $anon$1@110de08

scala> b.value // (3 * 2) + 3 : b -> Add -> Mult -> ValueImpl
add
mult
valueimpl
res91: Int = 9

12.6 Why not multiple inheritance?

kingori

trait는 다중상속과 유사하지만 차이점도 있음

  • super : 다중상속에서 super.method() 의 method()는 대상이 바로 결정이 되지만, trait에선 class와 trait들의 linearization을 거쳐 대상이 결정됨. 따라서 stackable modification이 가능함.
    • 다중상속에서 super를 만나면 어떤 method를 호출해야 하는 지 애매한 문제를 linealization으로 해결
  • linearization: classs, 상속 class, trait들을 줄 세운 다음, super 호출이 이뤄지면 메서드들을 줄줄이 쌓아둠.
    • linearization 순서 자체는 복잡함. 그냥 대충 먼저 언급된 type부터 쌓인다고 알아둬도 큰 무리 없음.
    • class는 superclass, trait보다 먼저 나옴
abstract class Animal { def cry():Unit }
trait Furry extends Animal { abstract override def cry()  { println("furry");super.cry()}} 
trait HasLegs extends Animal { abstract override  def cry()  { println("hasLegs");super.cry()} }
trait FourLegged extends HasLegs { abstract override  def cry() { println("fourLegged");super.cry()} }
class Doggy extends Animal { override def cry() { println("wuff!");}}
class DetailDoggy extends Doggy with Furry with FourLegged { override def cry() { println("detail");super.cry()}}

scala> new DetailDoggy ().cry() // DetailDoggy -> FourLegged -> HasLegs -> Furry -> Doggy
detail
fourLegged
hasLegs
furry
wuff!
  • abstract override 쌓는 순서 고민을 잘 해야 할 듯. 그냥 앞 결과를 뒤로 보낸다고 생각하면 될 듯? A -> B -> C 라면
    • 실행 순서는 A -> B -> C. 실행되면서 stack에 쌓이고 super를 호출하면 다음 녀석이 호출. super를 호출 안하면?
abstract class A { def a():Unit }
trait B extends A { abstract override def a():Unit = { println("a") } }
class C extends A { override def a():Unit = { println("c") } }
class D extends C with B

scala> new D().a  // D-> B -> C -> A. B에서 a 찍고 super 호출 안하니 그냥 끝내버림. **조심하자**
a

12.7 To trait, or not to trait?

kingori

trait와 abstract class, concrete class 를 선택하는 가이드.

  • concrete class: 행위를 재사용하지 않을 경우, 성능이 중요한 경우
  • trait: 서로 관련성 없는 class 사이에서 재사용되어야 하는 경우
  • abstract class
    • java code에서 재사용해야 할 경우. 참고: abstract member만 가진 trait는 java의 interface로 생성됨
    • compile된 형태로 배포할 경우. trait가 수정될 경우엔 이 trait를 상속하는 모든 class가 다시 compile되어야 하므로. 근데 이게 무슨 소리지? If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine.

딱히 위 경우로 판단하기 어렵다면 trait!

12.8 Conclusion

kingori

skip

Clone this wiki locally