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 |