예를 들어 (공동, 대조 및 in) 분산이 어떻게 작동합니까?
이 질문 에 이어 누군가 Scala에서 다음을 설명 할 수 있습니다.
class Slot[+T] (var some: T) {
// DOES NOT COMPILE
// "COVARIANT parameter in CONTRAVARIANT position"
}
형식 선언 +T
과 T
형식 선언 의 차이점을 이해합니다 (을 사용하면 컴파일됩니다 T
). 하지만 어떻게 하나 실제로 일을 만들기에 의지하지 않고 형식 매개 변수의 공변 인 클래스 쓰기 않습니다 unparametrized를 ? 의 인스턴스로만 다음을 만들 수 있는지 어떻게 확인할 수 T
있습니까?
class Slot[+T] (var some: Object){
def get() = { some.asInstanceOf[T] }
}
편집 -이제 이것을 아래로 가져 왔습니다.
abstract class _Slot[+T, V <: T] (var some: V) {
def getT() = { some }
}
이것은 모두 좋지만 이제는 두 가지 유형의 매개 변수가 있습니다. 다음과 같이 질문을 다시하겠습니다.
유형 이 공변 인 인 불변 Slot
클래스를 작성하려면 어떻게 해야합니까?
편집 2 : 어! 내가 사용 var
하지 val
. 다음은 내가 원하는 것입니다.
class Slot[+T] (val some: T) {
}
일반적으로 공변량 유형 매개 변수는 클래스가 하위 유형화됨에 따라 범위가 변경 될 수있는 유형입니다 (또는 하위 유형에 따라 달라 지므로 "co-"접두어). 보다 구체적으로 :
trait List[+A]
List[Int]
의 하위 유형 List[AnyVal]
이기 때문에 Int
의 하위 유형입니다 AnyVal
. 이는 List[Int]
유형 값 List[AnyVal]
이 예상 되는 인스턴스를 제공 할 수 있음을 의미합니다 . 이것은 제네릭이 작동하는 데 매우 직관적 인 방법이지만 변경 가능한 데이터가있을 때 소리가 나지 않습니다 (유형 시스템이 깨짐). 이것이 제네릭이 Java에서 변하지 않는 이유입니다. Java 배열을 사용하는 소리가 들리지 않는 간단한 예 (공변량이 잘못됨) :
Object[] arr = new Integer[1];
arr[0] = "Hello, there!";
방금 type의 값을 type String
의 배열에 할당했습니다 Integer[]
. 명백한 이유 때문에 이것은 나쁜 소식입니다. Java 타입 시스템은 실제로 컴파일 타임에 이것을 허용합니다. JVM은 "유용하게" ArrayStoreException
런타임에 던질 것 입니다. 스칼라의 타입 시스템은 Array
클래스 의 타입 매개 변수 가 변하지 않기 때문에이 문제를 방지합니다 (선언이 [A]
아닌 [+A]
).
contravariance 라고하는 다른 유형의 분산이 있습니다 . 공분산으로 인해 일부 문제가 발생할 수있는 이유를 설명하므로 매우 중요합니다. 공분산은 말 그대로 공분산의 반대입니다. 매개 변수 는 하위 유형에 따라 위쪽 으로 다양 합니다. 매우 직관적 인 응용 프로그램이 있기는하지만 반 직관적이기 때문에 부분적으로 덜 일반적입니다.
trait Function1[-P, +R] {
def apply(p: P): R
}
type 매개 변수 에 " - "분산 주석이 P
있습니다. Function1
전체적으로이 선언 은에서 P
공변량 및 공변량을 의미합니다 R
. 따라서 다음과 같은 공리를 도출 할 수 있습니다.
T1' <: T1
T2 <: T2'
---------------------------------------- S-Fun
Function1[T1, T2] <: Function1[T1', T2']
공지 T1'
의 아형 (또는 동일한 타입)이어야 T1
그것이 대한 반대 인 반면에, T2
그리고 T2'
. 영어로 다음과 같이 읽을 수 있습니다.
함수 A는 다른 기능의 하위 유형 B 의 파라미터를 입력하면 , A는 의 파라미터 형태의 슈퍼이고 B가 의 반환 입력 중 A는 의 리턴 타입의 서브 타입 인 B .
이 규칙의 이유는 독자에게 연습으로 남겨두고 있습니다. (힌트 : 위의 배열 예제와 같이 함수가 하위 유형화되므로 다른 경우에 대해 생각하십시오).
공분산 및 반공 분산에 대한 새로운 지식을 통해 다음 예제가 컴파일되지 않는 이유를 확인할 수 있습니다.
trait List[+A] {
def cons(hd: A): List[A]
}
문제는 A
공변량이지만 cons
함수는 유형 매개 변수가 변하지 않을 것으로 예상합니다 . 따라서 A
잘못된 방향으로 변화하고 있습니다. 흥미롭게도, 우리는에 List
반 변형 을 만들어서이 문제를 해결할 수는 A
있지만 List[A]
, cons
함수가 반환 유형이 공변 이 될 것으로 기대하기 때문에 반환 유형 은 유효하지 않습니다 .
여기서 우리의 유일한 두 가지 옵션은 a) A
불변성을 만들고 공분산의 훌륭하고 직관적 인 하위 입력 속성을 잃거나 b) 하한으로 cons
정의 A
되는 메서드에 지역 유형 매개 변수를 추가하는 것입니다 .
def cons[B >: A](v: B): List[B]
This is now valid. You can imagine that A
is varying downward, but B
is able to vary upward with respect to A
since A
is its lower-bound. With this method declaration, we can have A
be covariant and everything works out.
Notice that this trick only works if we return an instance of List
which is specialized on the less-specific type B
. If you try to make List
mutable, things break down since you end up trying to assign values of type B
to a variable of type A
, which is disallowed by the compiler. Whenever you have mutability, you need to have a mutator of some sort, which requires a method parameter of a certain type, which (together with the accessor) implies invariance. Covariance works with immutable data since the only possible operation is an accessor, which may be given a covariant return type.
@Daniel has explained it very well. But to explain it in short, if it was allowed:
class Slot[+T](var some: T) {
def get: T = some
}
val slot: Slot[Dog] = new Slot[Dog](new Dog)
val slot2: Slot[Animal] = slot //because of co-variance
slot2.some = new Animal //legal as some is a var
slot.get ??
slot.get
will then throw an error at runtime as it was unsuccessful in converting an Animal
to Dog
(duh!).
In general mutability doesn't go well with co-variance and contra-variance. That is the reason why all Java collections are invariant.
See Scala by example, page 57+ for a full discussion of this.
If I'm understanding your comment correctly, you need to reread the passage starting at the bottom of page 56 (basically, what I think you are asking for isn't type-safe without run time checks, which scala doesn't do, so you're out of luck). Translating their example to use your construct:
val x = new Slot[String]("test") // Make a slot
val y: Slot[Any] = x // Ok, 'cause String is a subtype of Any
y.set(new Rational(1, 2)) // Works, but now x.get() will blow up
If you feel I'm not understanding your question (a distinct possibility), try adding more explanation / context to the problem description and I'll try again.
In response to your edit: Immutable slots are a whole different situation...* smile * I hope the example above helped.
You need to apply a lower bound on the parameter. I'm having a hard time remembering the syntax, but I think it would look something like this:
class Slot[+T, V <: T](var some: V) {
//blah
}
The Scala-by-example is a bit hard to understand, a few concrete examples would have helped.
'programing tip' 카테고리의 다른 글
빌드시 코드 4로 명령 복사가 종료되었습니다.-Visual Studio를 다시 시작하면 문제가 해결됩니다. (0) | 2020.06.14 |
---|---|
AVAudioRecorder를 사용하여 iPhone에서 오디오를 녹음하려면 어떻게합니까? (0) | 2020.06.14 |
파이 명령 출력을 티로 출력하지만 명령의 종료 코드도 저장합니다 (0) | 2020.06.14 |
이동하는 생성자 (0) | 2020.06.14 |
가장 컴팩트 한 매핑을 만드는 방법 n → isprime (n) 최대 한계 N? (0) | 2020.06.14 |