이 질문 에 이어 누군가 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!";
우리는 방금 유형의 값을 할당했습니다. String
의 배열에Integer[]
. 명백한 이유 때문에 이것은 나쁜 소식입니다. Java 타입 시스템은 실제로 컴파일 타임에 이것을 허용합니다. JVM은 “유용하게” ArrayStoreException
런타임에 던질 것이다 . 스칼라의 타입 시스템은 Array
클래스 의 타입 매개 변수 가 변하지 않기 때문에이 문제를 방지합니다 (선언이 [A]
아닌 [+A]
).
반공 분산으로 알려진 다른 유형의 분산이 있습니다. . 공분산으로 인해 일부 문제가 발생할 수있는 이유를 설명하므로 매우 중요합니다. 공분산은 말 그대로 공분산의 반대입니다. 매개 변수 는 하위 유형에 따라 위쪽 으로 다양 합니다. 매우 직관적 인 응용 프로그램이 있기는하지만 반 직관적이기 때문에 부분적으로 덜 일반적입니다.
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]
이제 유효합니다. 당신은 그것을 상상할 수 있습니다A
아래로B
수 있지만,A
A
그것의 하한 이기 때문에 . 이 메소드 선언으로A
공변량을 수 있으며 모든 것이 잘 작동합니다.
이 트릭 List
은 덜 구체적인 유형에 특화된 인스턴스를 반환하는 경우에만 작동합니다 B
. List
변경 가능 하게 만들 려고하면 type 값을 type B
변수에 할당하려고 시도하기 때문에 문제 A
가 발생합니다. 이는 컴파일러에서 허용하지 않습니다. 변경 가능성이있을 때마다 (종종 접근 자와 함께) 불변성을 암시하는 특정 유형의 메소드 매개 변수가 필요한 일종의 뮤 테이터가 필요합니다. 공분산은 변경 불가능한 데이터와 함께 작동합니다. 가능한 유일한 조작은 접근 자이므로 공변량 리턴 유형이 제공 될 수 있습니다.
답변
@Daniel은 그것을 잘 설명했습니다. 그러나 허용 된 경우 간단히 설명하십시오.
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
그런 다음에 변환에 실패하여 런타임에 오류가 발생 Animal
합니다.Dog
(duh!) 합니다.
일반적으로 변이성은 공분산 및 반 분산과 잘 맞지 않습니다. 이것이 모든 Java 콜렉션이 변하지 않는 이유입니다.
답변
이에 대한 자세한 설명은 57 페이지 스칼라를 참조하십시오 .
귀하의 의견을 올바르게 이해하고 있다면 56 페이지 하단에서 시작하는 구절을 다시 읽어야합니다 (기본적으로, 당신이 요구하는 것은 런타임 검사없이 유형 안전하지 않다는 것입니다. 그래서 당신은 운이 없어요). 구문을 사용하기 위해 예제를 번역 :
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
귀하의 질문을 이해하지 못한다고 생각되는 경우 (고유 한 가능성), 문제 설명에 설명 / 문맥을 추가해보십시오. 다시 시도하겠습니다.
편집에 대한 응답으로 : 불변 슬롯은 완전히 다른 상황입니다 … * smile * 위의 예가 도움이 되었기를 바랍니다.
답변
매개 변수에 하한을 적용해야합니다. 구문을 기억하는 데 어려움을 겪고 있지만 다음과 같이 보일 것입니다.
class Slot[+T, V <: T](var some: V) {
//blah
}
스칼라 예제는 이해하기 약간 어렵지만 몇 가지 구체적인 예제가 도움이 될 것입니다.