2013-09-06 4 views
0

내가 이해하지 못하는 것을 보았습니다. 하나는 방법을 적용 이상이있을 때, 당신은 반환 유형을 지정해야합니다스칼라 : 매개 변수 및 상속 유형

abstract class VehicleReader[T <: Vehicle] { 
... 

object VehicleReader { 
    def apply[T <: Vehicle](vehicleId: Int): VehicleReader[T] = apply(vehicleType(vehicleId)) 

    def apply[T <: Vehicle](vehicleType VehicleType): VehicleReader[T] = vehicleType match { 
    case VehicleType.Car => new CarReader().asInstanceOf[VehicleReader[T]] 
    ... 

참고 : 방법을 적용하여 내가 (말) 차량의 계층 구조, VehicalReaders의 해당 계층 구조 및 VehicleReader 객체가 있습니다. 반환 형식을 지정할 필요가없는 경우에는 아무런 문제가 없습니다.

이 캐스트 (.asInstanceOf [VehicleReader [T]])이 질문에 대한 이유입니다 -없는 결과가 같은 오류를 컴파일 할 수 있습니다 :

type mismatch; 
found : CarReader 
required: VehicleReader[T] 
    case VehicleType.Car => new CarReader() 
           ^

관련 질문 :

  • 을 왜 할 수 없습니다 컴파일러는 CarReader를 VehicleReader [T]로 본다.
  • 이 상황에서 사용할 올바른 형식 매개 변수 및 반환 형식은 무엇입니까?

여기서 근본 원인은 VehicleReader가 type 매개 변수에 대해 불변하다는 것입니다. 그러나 공변량을 사용하면 결과가 변경되지 않습니다.

나는 이것이 다소 단순해야한다고 생각합니다. (예 : 와일드 카드로 자바에서 쉽게 구현할 수 있습니다.)

답변

4

이 문제는 매우 단순한 원인이 있으며 실제로는 분산과 관련이 없습니다. 더 간단한 예제를 고려해보십시오.

object Example { 
    def gimmeAListOf[T]: List[T] = List[Int](10) 
} 

이 스 니펫은 코드의 주요 아이디어를 포착합니다. 하지만 정확하지 않습니다.

val list = Example.gimmeAListOf[String] 

list의 유형은 무엇입니까? 메서드는 List[String]에 대해 구체적으로 요청했지만 항상 List[Int](10)을 반환합니다. 분명히 이것은 오류입니다. "을 당신은 내가 Example[T]의 인스턴스를 반환합니다 줘 T 어떤 유형"이 방법은 정말 선언 method[T]: Example[T] 같은 서명이있는 경우

그래서 단어에 넣어합니다. 이러한 유형은 때로는 '보편적으로 정량화 된'또는 단순히 '보편적'이라고 불린다.

그러나 이것은 사용자의 경우가 아닙니다. 함수는 해당 매개 변수의 값에 따라 VehicleReader[T]의 특정 인스턴스를 반환합니다. CarReader (어느 것으로 추정 되나요? VehicleReader[Car]). 내가 좋아하는 뭔가를 썼다 가정 : 컴파일러는 행복이 컴파일

class House extends Vehicle 

val reader = VehicleReader[House](VehicleType.Car) 
val house: House = reader.read() // Assuming there is a method VehicleReader[T].read(): T 

을하지만,이 코드가 실행될 때 나는 ClassCastException을 얻을 것이다.

두 가지 가능한 수정 방법이 있습니다.첫째, 당신은 자바 와일드 카드의보다 강력한 버전으로 생각 될 수있는 입력 존재 (또는 존재 적 정량)를 사용할 수 있습니다 :이 기능에 대한

def apply(vehicleType: VehicleType): VehicleReader[_] = ... 

서명은 기본적으로 읽기

는 "당신이 VehicleType 나에게주고 내가 당신에게 돌아 일부 유형의 경우 VehicleReader 의 인스턴스입니다. 유형이 VehicleReader[_] 인 개체를 갖게됩니다. 이 유형이 존재한다는 것을 제외하고는 매개 변수의 유형에 대해 아무 것도 말할 수 없기 때문에 이러한 유형을 실존 적으로 호출합니다.

def apply(vehicleType: VehicleType): VehicleReader[T] forSome {type T} = ... 

이 동등한 정의하고 이러한 유형의 이러한 특성을 가지고 왜 아마 더 분명하다 - 당신이 그것에 대해 아무것도 모르는, 그래서 T 유형, 매개 변수의 내부에 숨겨져 있지만 존재 않습니다.

그러나 실존 정보의이 속성으로 인해 실제 유형 매개 변수에 대한 정보를 얻을 수 없습니다. 예를 들어 VehicleReader[Car]이 을 벗어나는 경우/ClassTag을 유형 매개 변수로 사용하여 VehicleReader에 저장하고 캐스트하기 전에 확인하지 않는 한, 직접적인 캐스트 (asInstanceOf)를 사용하는 경우는 예외입니다. 때때로 (실제로, 대부분의 경우) 다루기 힘들 때가 있습니다.

두 번째 옵션은 구조에 대한 것입니다.

VehicleType.Car -> CarReader (<: VehicleReader[Car]) 
VehicleType.Truck -> TruckReader (<: VehicleReader[Truck]) 

등 : 명확한 코드에서 VehicleTypeVehicleReader[T] 간의 일치 성, 즉 당신이 VehicleType의 특정 인스턴스가있을 때 당신은 확실히 콘크리트 TVehicleReader[T]에 서명을 알고있다.

이 때문에 유형 매개 변수를 VehicleType에 추가하는 것이 좋습니다. 이 경우 귀하의 방법은 이제 입력 유형 및 출력 유형이 직접 연결되어

def apply[T <: Vehicle](vehicleType: VehicleType[T]): VehicleReader[T] = ... 

처럼 보일 것이고,이 방법의 사용자는 자신이 원하는 것을 T에 대한 VehicleType[T]의 올바른 인스턴스를 제공하도록 강요 될 것이다. 이것은 이전에 언급 한 런타임 오류를 배제합니다.

그래도 asInstanceOf 캐스팅이 필요합니다.

sealed trait VehicleType[T <: Vehicle] { 
    def newReader: VehicleReader[T] 
} 

object VehicleType { 
    case object Car extends VehicleType[Car] { 
    def newReader = new CarReader 
    } 
    // ... and so on 
} 

을 다음 : 당신이 VehicleType[T] 형식 매개 변수의 실제 값을 알고있는 유일한 곳이 유형의 인스턴스가 구축되는 곳이기 때문에 완전히지지 않도록하려면, VehicleTypeVehicleReader 인스턴스 코드 (예를 들어, 당신 new CarReader())를 이동해야합니다 VehicleReader 팩토리 메소드는 매우 깨끗보고 완전히 형태 보증 할 것이다 : 나는 또한 반환 형식의 자리를 시도했다

object VehicleReader { 
    def apply[T <: Vehicle](vehicleType: VehicleType[T]) = vehicleType.newReader 
} 
+0

(즉 실존)하지만 다음주의로 캐스트 할 수 반환 값을 요구하는 결과 . 당신의 두 번째 제안을 탐구 할 것입니다. –

+0

@ScalaNewb, 두 번째 접근법에 대해 자세히 설명했습니다. –