Scala Language
Implicits
수색…
통사론
- 암시 적 val x : T = ???
비고
암시 적 클래스를 사용하면 코드를 수정하지 않고도 기존 메서드에 사용자 지정 메서드를 추가 할 수 있으므로 코드를 제어하지 않고도 형식을 풍부하게 만들 수 있습니다.
암시 적 유형을 사용하여 기존 클래스를 풍부하게하는 것은 흔히 '내 라이브러리를 풍부하게합니다'패턴이라고합니다.
암시 적 클래스에 대한 제한 사항
- 암시 적 클래스는 다른 클래스, 객체 또는 특성 내에 만 존재할 수 있습니다.
- 암시 적 클래스는 암시 적이 아닌 기본 생성자 매개 변수 하나만 가질 수 있습니다.
- 암시 적 클래스와 이름이 같은 동일한 범위 내에 다른 객체, 클래스, 특성 또는 클래스 멤버 정의가 없을 수 있습니다.
암시 적 변환
암시 적 변환을 사용하면 컴파일러는 한 유형의 객체를 다른 유형으로 자동 변환 할 수 있습니다. 이렇게하면 코드가 객체를 다른 유형의 객체로 처리 할 수 있습니다.
case class Foo(i: Int)
// without the implicit
Foo(40) + 2 // compilation-error (type mismatch)
// defines how to turn a Foo into an Int
implicit def fooToInt(foo: Foo): Int = foo.i
// now the Foo is converted to Int automatically when needed
Foo(40) + 2 // 42
변환은 단방향입니다 :이 경우 42
를 Foo(42)
다시 변환 할 수 없습니다. 이렇게하려면 두 번째 암시 적 변환을 정의해야합니다.
implicit def intToFoo(i: Int): Foo = Foo(i)
이것은 float 값을 정수 값에 추가 할 수있는 메커니즘입니다.
묵시적 전환은 일어나는 일을 난처하게하기 때문에 조심스럽게 사용해야합니다. 암시 적 변환을 사용하여 명백한 가독성 이득이 없으면 메서드 호출을 통해 명시 적 변환을 사용하는 것이 가장 좋습니다.
암시 적 변환에 중요한 성능 영향은 없습니다.
스칼라는 자바에서 스칼라로 변환하는 모든 변환을 포함하여 scala.Predef
에서 다양한 암시 적 변환을 자동으로 가져옵니다. 이는 기본적으로 모든 파일 편집에 포함됩니다.
암시 적 매개 변수
암시 적 매개 변수는 유형의 매개 변수가 범위에서 한 번 정의 된 다음 해당 유형의 값을 사용하는 모든 함수에 적용되어야하는 경우 유용 할 수 있습니다.
일반적인 함수 호출은 다음과 같습니다.
// import the duration methods
import scala.concurrent.duration._
// a normal method:
def doLongRunningTask(timeout: FiniteDuration): Long = timeout.toMillis
val timeout = 1.second
// timeout: scala.concurrent.duration.FiniteDuration = 1 second
// to call it
doLongRunningTask(timeout) // 1000
이제 모든 메소드에 타임 아웃 지속 시간이 있으며, 동일한 타임 아웃을 사용하여 모든 메소드를 호출하려고합니다. 시간 제한을 암시 적 변수로 정의 할 수 있습니다.
// import the duration methods
import scala.concurrent.duration._
// dummy methods that use the implicit parameter
def doLongRunningTaskA()(implicit timeout: FiniteDuration): Long = timeout.toMillis
def doLongRunningTaskB()(implicit timeout: FiniteDuration): Long = timeout.toMillis
// we define the value timeout as implicit
implicit val timeout: FiniteDuration = 1.second
// we can now call the functions without passing the timeout parameter
doLongRunningTaskA() // 1000
doLongRunningTaskB() // 1000
이것이 작동하는 방식은 scalac 컴파일러가 암시 적 으로 표시되고 범위가 암시 적 매개 변수 중 하나 와 일치 하는 범위의 값을 찾는 것입니다. 발견되면 암시 적 매개 변수로 적용됩니다.
범위에서 같은 유형의 두 개 또는 그 이상의 함축을 정의하면이 기능이 작동하지 않습니다.
오류 메시지를 사용자 정의하려면 유형에 implicitNotFound
주석을 사용하십시오.
@annotation.implicitNotFound(msg = "Select the proper implicit value for type M[${A}]!")
case class M[A](v: A) {}
def usage[O](implicit x: M[O]): O = x.v
//Does not work because no implicit value is present for type `M[Int]`
//usage[Int] //Select the proper implicit value for type M[Int]!
implicit val first: M[Int] = M(1)
usage[Int] //Works when `second` is not in scope
implicit val second: M[Int] = M(2)
//Does not work because more than one implicit values are present for the type `M[Int]`
//usage[Int] //Select the proper implicit value for type M[Int]!
타임 아웃은 이것에 대한 일반적인 사용 사례입니다. 예를 들어 Akka 에서 ActorSystem은 (대부분의 경우) 항상 동일하므로 대개 암묵적으로 전달됩니다. 또 하나의 유스 케이스는 라이브러리 디자인이며, 가장 일반적으로 typeclasses (예 : 스 카즈 , 고양이 또는 휴거 )에 의존하는 FP 라이브러리를 사용합니다.
일반적으로 Int , Long , String 등의 기본 유형에 암시 적 매개 변수를 사용하는 것은 나쁜 습관으로 간주됩니다. 혼동을 일으키고 코드를 읽기 쉽게 만듭니다.
암시 적 클래스
암시 적 클래스를 사용하면 이전에 정의한 클래스에 새 메서드를 추가 할 수 있습니다.
String
클래스에는 withoutVowels
메서드가 없습니다. 다음과 같이 추가 할 수 있습니다.
object StringUtil {
implicit class StringEnhancer(str: String) {
def withoutVowels: String = str.replaceAll("[aeiou]", "")
}
}
암시 적 클래스에는 확장하려는 유형 ( String
)과 유형 ( withoutVowels
)에 "추가"할 메소드가 포함 된 단일 생성자 매개 변수 ( str
)가 있습니다. 새로 정의 된 메서드를 향상된 형식에서 직접 사용할 수 있습니다 (향상된 형식이 암시 적 범위에있는 경우).
import StringUtil.StringEnhancer // Brings StringEnhancer into implicit scope
println("Hello world".withoutVowels) // Hll wrld
후드 아래에서 암시 적 클래스는 다음과 같이 향상된 형식에서 암시 적 클래스로의 암시 적 변환 을 정의합니다.
implicit def toStringEnhancer(str: String): StringEnhancer = new StringEnhancer(str)
암시 적 클래스는 종종 런타임 객체를 만들지 않으므로 런타임 오버 헤드를 제거하기 위해 값 클래스 로 정의됩니다.
implicit class StringEnhancer(val str: String) extends AnyVal {
/* conversions code here */
}
위의 향상된 정의를 사용하면 withoutVowels
메서드가 호출 될 때마다 StringEnhancer
의 새 인스턴스를 만들 필요가 없습니다.
'암시 적으로'암시 적 매개 변수를 사용하여 해결
하나 이상의 암시 적 매개 변수가있는 암시 적 매개 변수 목록을 가정합니다.
case class Example(p1:String, p2:String)(implicit ctx1:SomeCtx1, ctx2:SomeCtx2)
이제 암시 적 인스턴스 중 하나를 사용할 수없는 경우 ( SomeCtx1
) 필요한 다른 모든 암시 적 인스턴스가 범위 내에있는 경우 클래스의 인스턴스를 만들려면 SomeCtx1
인스턴스를 제공해야합니다.
이것은 implicitly
키워드를 사용하여 범위 내 암시 적 인스턴스를 서로 보존하면서 수행 할 수 있습니다.
Example("something","somethingElse")(new SomeCtx1(), implicitly[SomeCtx2])
REPL의 내포
REPL 세션 중에 범위 내 모든 implicits
를 보려면 :
scala> :implicits
Predef.scala
정의 된 암시 적 변환도 포함하려면 다음을 Predef.scala
.
scala> :implicits -v
표현식을 가지고 있고 그것에 적용되는 모든 다시 쓰기 규칙의 효과를 보려면 (implicit 포함) :
scala> reflect.runtime.universe.reify(expr) // No quotes. reify is a macro operating directly on code.
(예:
scala> import reflect.runtime.universe._
scala> reify(Array("Alice", "Bob", "Eve").mkString(", "))
resX: Expr[String] = Expr[String](Predef.refArrayOps(Array.apply("Alice", "Bob", "Eve")(Predef.implicitly)).mkString(", "))
)