Programming in Sala 책으로 스칼라 스터디하면서 정리했던 내용이다. 지금은 3판 번역본도 나왔지만, 약간 앞서서 스터디를 시작해서 2판으로 진행했다.
21장
암시적 변환
암시적 변환 예
- 서로를 고려하지 않은 두 독립 소프트웨어를 한데 묶는데 유용
 
// (1) 자바 코드를 스칼라 코드로 그대로 포팅
val button = new JButton
button.addActionListener(
  new ActionListener {
    def actionPerformed(event: ActionEvent) {
      println("pressed!")
    }
  }
)
// 암시적 변환 코드
implicit deff function2ActionListener(f: ActionEvent => Unit) =
  new ActionListener {
    def actionPerformed(event: ActionEvent) = f(event)
  }
// (2) 변환 코드 사용
button.addActionListener(
  function2ActionListener(
    (_: ActionEvent) => println("pressed!")
  )
)
// (3) 암시적 변환 사용
button.addActionListener(
  (_: ActionEvent) => println("pressed!")
)
암시적 변환 규칙
- 표시 규칙 : implicit로 표시한 정의만 검토 대상이다
 - 스코프 규칙 : 삽입할 implicit 변환은 스코프 내에 단일 식별자로만 존재하거나, 변환의 결과나 원래 타입과 연관이 있어야 한다(단, 원 타입이나 변환 결과 타입의 동반 객체에 있는 암시적 정의도 검토 대상임)
 - 한번에 하나만 규칙 : 오직 하나의 암시적 선언만 사용한다
 - 명시성 유선 규칙 : 코드가 그 상태 그대로 타입 검사를 통과한다면 암시를 통한 변환을 시도하지 않는다
 
암시적 변환 이름
- 명시적으로 변환을 사용할 때 사용
 - 특정 지점에 사용 가능한 암시적 변환을 확인해야할 경우(사용하는 변환만 임포트)
 
암시가 사용되는 부분
- 예상 타입 암시적 변환 : 
implicit def int2double(x: Int): Double = x.toDouble - 호출 대상 객체 변환 : 
"abc".exists === stringWrapper("abc").exists 
// 호출 대상 객체 변환 - 새 타입과 함께 통합
implicit def intToRational(x: Int) = new Rational(x, 1)
val oneHalf = new Rational(1, 2)
1 + oneHalf
// Rational = 3/2
// 호출 대상 객체 변환 - 새로운 문법 흉내 내기
package scala
object Predef {
  class ArrowAssoc[A](x: A) {
    def -> [B](y: B): Tuple2[A, B] = Tuple2(x, y)
  }
  implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
}
Map(1 -> "one", 2 -> "two", 3 -> "three")
- 암시적 파라미터
 
암시적 파라미터
// 암시적 파라미터 예
class PreferredPrompt(val preference: String)
object Greeter {
  def greet(name: String)(implicit prompt: PreferredPrompt) {
    println("Welcome, "+ name +". The system is ready.")
    println(prompt.preference)
  }
}
// 명시적 사용 예
val bobsPrompt = new PreferredPrompt("relax> ")
Greeter.greet("Joe")(bobsPrompt)
// 암시적 파라미터 사용 예
object JoesPrefs {
  implicit val prompt = new Preferredprompt("Yes, master> ")
}
import JoesPrefs._
Greeter.greet("Joe")
// 암시적 파라미터 예 - 두개 파라미터
class PreferredPrompt(val preference: String)
class PreferredDrink(val preference: String)
object Greeter {
  def greet(name: String)(implicit prompt: PreferredPrompt, drink: PreferredDrink) {
    println("Welcome, "+ name +". The system is ready.")
    print("But while you work, ")
    println("why not enjoy a cup of "+ drink.preference +"?")
    println(prompt.preference)
  }
}
object JoesPrefs {
  implicit val prompt = new PreferredPrompt("Yes, master> ")
  implicit val drink = new PreferredDrink("tea")
}
import JoesPrefs._
// 명시적 사용 예
Greeter.greet("joe")(prompt, drink)
// 두 암시적 파라미터 사용 예
import JoesPrefs._
Greeter.greet("joe")
- 암시적 파라미터 타입은 충분히 드물거나 특별해야한다. -> 암시적 파라미터의 타입 안에는 역할을 알려주는 이름을 최소한 하나 이상 사용하는 것이 좋다.
 - 암시적 파라미터는 앞쪽 파라미터 목록에서 명시적으로 사용한 인자 타입에 대한 정보를 제공하는 경우에 많이 사용된다.
 
// 원소 타입이 Ordered의 서브 타입이 아닌 리스트에서 사용할 수 없음!
def maxListUpBound[T <: Ordered[T]](elements: List[T]): T =
  elements match {
    case List() => throw new IllegalArgumentException("empty list!")
    case List(x) => x
    case x :: rest =>
      val maxRest = maxListUpBound(rest)
      if (x > maxRest) x
      else maxRest
  }
// 암시적 파라미터 사용
def maxListImpParm[T](elements: List[T])(implicit orderer: T => Ordered[T]): T =
  elements match {
    case List() => throw new IllegalArgumentException("empty list!")
    case List(x) => x
    case x :: rest =>
      val maxRest = maxListImpParm(rest)(orderer)
      if (orderer(x) > maxRest) x
      else maxRest
  }
뷰 바운드
- 파라미터에 대해 implicit을 사용하는 경우, 메소드 본문 안에서 implicit 값으로 취급됨
 
// 메소드 본문 안에서 implicit 사용
def maxList[T](elements: List[T])(implicit orderer: T => Ordered[T]): T =
  elements match {
    case List() => throw new IllegalArgumentException("empty list!")
    case List(x) => x
    case x :: rest =>
      val maxRest = maxListImpParm(rest)    // orderer
      if (x > maxRest) x                    // orderer(x)
      else maxRest
  }
// 뷰 바운드 사용
def maxList[T <% Ordered[T]](elements: List[T]): T =
  elements match {
    case List() => throw new IllegalArgumentException("empty list!")
    case List(x) => x
    case x :: rest =>
      val maxRest = maxListImpParm(rest)    // orderer
      if (x > maxRest) x                    // orderer(x)
      else maxRest
  }
T <% Ordered[T]: T를 Ordered[T]로 다룰 수 있는 모든 T 타입을 사용할 수 있다- 타입 T가 이미 Ordered[T]인 경우에는 Predef의 identity 함수 사용
 
implicit def identity[A](x: A): A = x // 받은 객체를 그대로 반환함
적용 가능한 암시적 변환이 여러개인 경우
- ~ Scala 2.7 : 컴파일 오류
 - Scala 2.8 : 더 구체적인 변환 적용
- 인자 타입이 다른 것의 서브 타입이다
 - 변환이 모두 메소드인데, 하나를 둘러싼 클래스가 다른 것을 둘러싼 클래스를 확장한다.
 
 
val cba = "abc".reverse
// 2.7 : cba는 컬렉션 - String -> 스칼라 컬렉션 변환
// 2.8 : cba는 String - 더 구체적인 String -> SringOps 변환 추가(reverse 메소드가 String 반환)
암시 디버깅
- 컴파일러가 암시를 찾지 못하는 경우, 명시적으로 변환을 써보고 오류가 발생하는지 확인한다.
 - 컴파일러에 
-Xprint:typer옵션 사용하여, 타입 검사기가 추가한 모든 암시적 변환이 있는 코드를 확인한다. 
'Developing' 카테고리의 다른 글
| Programming in Scala 스터디 정리 - 22장. 리스트 구현 (0) | 2017.10.14 | 
|---|---|
| Programming in Scala 스터디 정리 - 20장. 추상 멤버 (0) | 2017.10.09 | 
| Programming in Scala 스터디 정리 - 19장. 타입 파라미터화 (0) | 2017.10.01 |