Scala Language
유형 차이
수색…
공분산
+
기호는 유형 매개 변수를 공변 (covariant) 으로 표시합니다. 여기서 " Producer
는 A
공변 (covariant)"이라고 말합니다.
trait Producer[+A] {
def produce: A
}
공변 유형 매개 변수는 "출력"유형으로 생각할 수 있습니다. A
를 공변량으로 표시하면 Producer[X] <: Producer[Y]
가 X <: Y
제공한다고 주장합니다. 예를 들어 Producer[Cat]
는 유효한 모든 생산 된 고양이가 유효한 동물이기 때문에 유효한 Producer[Animal]
입니다.
공변량 유형 매개 변수는 반공 변 (입력) 위치에 나타날 수 없습니다. 다음 예제는 우리가 Co[Cat] <: Co[Animal]
이지만 Co[Cat]
은 def handle(a: Cat): Unit
Co[Animal]
요구하는대로 Animal
을 처리 할 수없는 def handle(a: Cat): Unit
을 가지고 있다고 컴파일 할 때 컴파일되지 않습니다!
trait Co[+A] {
def produce: A
def handle(a: A): Unit
}
이 제한 사항을 처리하기위한 한 가지 접근법은 공변 유형 매개 변수로 묶인 유형 매개 변수를 사용하는 것입니다. 다음 예에서 B
는 A
의 상위 유형임을 압니다. 따라서 주어진 Option[X] <: Option[Y]
에 대한 X <: Y
, 우리가 알고 Option[X]
의 def getOrElse[B >: X](b: => B): B
어떤 수퍼받을 수 X
- Option[Y]
필요로하는 Y
의 수퍼 유형을 포함합니다.
trait Option[+A] {
def getOrElse[B >: A](b: => B): B
}
불변성
기본적으로 모든 유형 매개 변수는 불변합니다. trait A[B]
주어지면 " A
는 B
불변합니다"라고 말합니다. 이것은 A[Cat]
과 A[Animal]
두 개의 매개 변수가 주어 졌음을 의미합니다. A[Cat] <: A[Animal]
또는 A[Cat] >: A[Animal]
Cat
와 Animal
사이의 관계에 관계없이 A[Cat] >: A[Animal]
.
분산 주석은 이러한 관계를 선언하는 수단을 제공하고 형식 매개 변수의 사용에 규칙을 적용하여 관계가 유효하게 유지되도록합니다.
반공립
-
기호는 유형 매개 변수를 반항 문자로 표시합니다. 여기에서 " Handler
는 A
반역입니다"라고 말합니다.
trait Handler[-A] {
def handle(a: A): Unit
}
contravariant type 매개 변수는 "입력"유형으로 생각할 수 있습니다. A
를 반 변형으로 표시하면 Handler[X] <: Handler[Y]
는 X >: Y
제공한다고 주장합니다. 예를 들어 Handler[Animal]
는 유효한 Handler[Cat]
이며 Handler[Animal]
도 고양이를 처리해야합니다.
반 변형 유형 매개 변수는 공변 (출력) 위치에 나타날 수 없습니다. 다음 예제는 Contra[Animal] <: Contra[Cat]
주장하고 있으므로 컴파일되지 않습니다 Contra[Animal] <: Contra[Cat]
그러나 Contra[Animal]
에는 def produce: Animal
Contra[Cat]
필요로하는 고양이를 생성 할 수없는 def produce: Animal
!
trait Contra[-A] {
def handle(a: A): Unit
def produce: A
}
그러나 해상도 오버로드의 목적을 위해 contravariance는 contravariant type 매개 변수에서 유형의 특수성을 반 직관적으로 반전시킵니다. Handler[Animal]
은 Handler[Cat]
보다 "더 구체적"인 것으로 간주됩니다.
형식 매개 변수에서 메서드를 오버로드 할 수 없으므로이 동작은 일반적으로 암시 적 인수를 해결할 때만 문제가됩니다. ofCat
의 반환 유형이 더 구체적 ofAnimal
의 다음 예제에서는 절대로 사용되지 않습니다.
implicit def ofAnimal: Handler[Animal] = ???
implicit def ofCat: Handler[Cat] = ???
implicitly[Handler[Cat]].handle(new Cat)
이 동작은 현재 scala.math.Ordering
으로 변경 될 예정이며 scala.math.Ordering
이 해당 유형 매개 변수 T
에 대해 변경되지 않는 이유는 (예를 들어) 한 가지 해결 방법은 typeclass를 불변으로 만들고 암시적인 정의를 해당 유형의 하위 클래스에 적용 할 이벤트에 입력 매개 변수로 설정하는 것입니다.
trait Person
object Person {
implicit def ordering[A <: Person]: Ordering[A] = ???
}
콜렉션의 공분산
콜렉션은 일반적으로 요소 유형 *에서 공변 (covariant)이기 때문에 수퍼 유형이 예상되는 곳에서는 서브 유형 모음을 전달할 수 있습니다.
trait Animal { def name: String }
case class Dog(name: String) extends Animal
object Animal {
def printAnimalNames(animals: Seq[Animal]) = {
animals.foreach(animal => println(animal.name))
}
}
val myDogs: Seq[Dog] = Seq(Dog("Curly"), Dog("Larry"), Dog("Moe"))
Animal.printAnimalNames(myDogs)
// Curly
// Larry
// Moe
그것은 마법처럼 보인다, 그러나 사실 수 없습니다 Seq[Dog]
예상하는 방법으로 허용됩니다 Seq[Animal]
: A (여기에 높은 kinded 유형의 전체 개념이다 Seq
의 형식 매개 변수에있는 공변).
*
반례는 표준 라이브러리의 집합입니다.
불변 식 특성에 대한 공분산
하나의 메소드가 전체 특성을 공변 (covariant)하는 대신 공변 인수 (covariant argument)를 허용하는 방법도 있습니다. 반역 위치에서 T
를 사용하기를 원하지만 여전히 공분산을 가지고 있기 때문에 이것은 필요할 수 있습니다.
trait LocalVariance[T]{
/// ??? throws a NotImplementedError
def produce: T = ???
// the implicit evidence provided by the compiler confirms that S is a
// subtype of T.
def handle[S](s: S)(implicit evidence: S <:< T) = {
// and we can use the evidence to convert s into t.
val t: T = evidence(s)
???
}
}
trait A {}
trait B extends A {}
object Test {
val lv = new LocalVariance[A] {}
// now we can pass a B instead of an A.
lv.handle(new B {})
}