2011-03-30 2 views
12

나는 특정 유형의 클래스를 반환하는 방법을하고 싶은,하지만 난 방법은 다음과 같이 클래스가 특정 특성을 확장 여부에 따라 다르게 작동 할 :스칼라에서 타입 매개 변수를 사용하여 특성을 구현하는지 확인할 수 있습니까?

 

case class ClassA extends TraitA 
case class ClassB extends TraitB 
case class ClassC extends TraitA 
... 
def myfunc[T]():T = { 
    T match { 
    case TraitA => // return new T in a particular way 
    case TraitB => // ditto 
    } 
} 
 

이 가능 , 또는 나는 그것에 대해 잘못된 길로 가고 있습니까?

감사합니다.

답변

13

비교할 항목이 없으므로 유형을 직접 비교할 수 없습니다 (런타임시 erasure로 인해). 당신은 당신의 클래스의 표현에 일할 수 :

trait TraitA { } 
trait TraitB { } 
class ClassA extends TraitA { } 
class ClassB extends TraitB { } 

def myFunc[T](clazz: Class[T]) = { 
    if (classOf[TraitA] isAssignableFrom clazz) println("A") 
    else if (classOf[TraitB] isAssignableFrom clazz) println("B") 
    else println("?") 
} 

scala> myFunc(classOf[ClassA]) 
A 

scala> myFunc(classOf[String]) 
? 

또는 수있는 클래스의 인스턴스에 패턴 일치 :

def myFunc2[T](t: T) = t match { 
    case _: TraitA => println("A") 
    case _: TraitB => println("B") 
    case _ => println("?") 
} 

scala> myFunc2(new ClassA) 
A 

scala> myFunc2(Some(5)) 
? 

또한 클래스를 통해 구문 적으로 덜 강요하는 방법으로 첫 번째 방법을 사용할 수 있습니다

def myFunc3[T](implicit mf: ClassManifest[T]) = { 
    val clazz = mf.erasure 
    if (classOf[TraitA] isAssignableFrom clazz) println("A") 
    else if (classOf[TraitB] isAssignableFrom clazz) println("B") 
    else println("?") 
} 

scala> myFunc3[ClassA] 
A 

scala> myFunc3[String] 
? 

당신은이/다른 휘두르기 쉬운 될 경우에도 경우 파견의 다른 종류를 선택할 수 있습니다 : 명단

object MyFunc { 
    val dispatch = Map(
    classOf[TraitA] -> (() => println("A")), 
    classOf[TraitB] -> (() => println("B")) 
) 
    val default =() => println("?") 
    def apply[T](implicit mf: ClassManifest[T]) = 
    dispatch.find(_._1 isAssignableFrom mf.erasure).map(_._2).getOrElse(default)() 
} 

scala> MyFunc[ClassA] 
A 

scala> MyFunc[String] 
? 

여기에서 사용하는 모든 일반 코드는 암시 적 매개 변수 또는 축약어로 [T: ClassManifest] 인 클래스 매니페스트를 사용할 수 있어야합니다.

+0

실제로 내 경우에는 잘 작동하는 2 가지 방법을 발견했습니다. 1) classOf [TraitA] isAssignableFrom clazz – codefly

+0

+1 : 처음에는 대답과 스칼라 모두에 짜증이났다. ("유형이 없기 때문에 유형을 직접 비교할 수 없다. ** * 아무것도 없다. * 비교할 곳이 있습니다 "... ** 유형 **이 있으며 유형은 확실합니다."값이 * 없습니다 * "라는 문구를 작성해야합니다. 그런데 클래스 매니페스트에 대해 설명해 주셨습니다. 감사합니다! – rsenna

+0

@rsenna - "아무 것도 없습니다"라는 말은 "T"는 컴파일러에게 타입을 컴파일 타임에 똑바로 유지해야하는 방법을 알려주기 때문에 거기에 아무것도 없다는 것을 의미했습니다. 그래서 실제로 ... 제네릭 타입은 런타임에 아무 것도 없습니다. 그것은 사라 졌어요. ("타입 소거.") Manifest는 런타임에 컴파일 타임 정보를 제공하는 방법을 제공합니다. ('ClassTag' 또는'TypeTag'는 2.10에서 그것을하기위한 새로운 방법입니다. 그러나 옛날 방식은 여전히 ​​작동합니다.) 어쨌든, 내 말씨가 아주 재치가 없더라도 당신이 유용하다는 것을 알았 기 때문에 기쁩니다! –

1

유형을 확인하려면 인스턴스가 필요합니다. 범용 그 자체는 형식에 대한 자리 표시 자일뿐입니다. 당신이 뭔가를 할 수 있습니다 :

trait bTrait //common base trait 
trait TraitA extends bTrait 
trait TraitB extends bTrait 

class ClassA extends TraitA 
class ClassB extends TraitB 

def myFunc[T <: bTrait](t:T) : String = //passing explicitly an instance of type T 
{ 
    t match { 
    case _ : TraitA => "TraitA" 
    case _ : TraitB => "TraitB" 
    } 
} 

println(myFunc(new ClassA)) //prints TraitA 
+0

동의하면 쉽게 인스턴스를 만들 수 있습니다! – codefly

2

이 거의 상속의 정규 설명입니다

"방법은 클래스가 특정 특성을 확장 여부에 따라 다르게 작동 할". 각기 다른 행동을 캡슐화하는 방법을 사용할 수 있습니까?

+0

여기서 중요한 문제는 객체의 인스턴스가 없다는 것입니다. 객체의 유형에 따라 어디로 가야할지 결정해야합니다 (이 경우 데이터베이스) – codefly

관련 문제