2012-12-12 3 views
2

저는 스칼라에서 GUI 이벤트 시스템을 구현하고 있습니다. 내가 좋아하는 뭔가가 있습니다스칼라에서 매개 변수로 클로저를 보유하는 데이터 구조

case class EventObject 
case class KeyEventObject extends EventObject 
case class MouseEventObject extends EventObject 

을 나는 그렇게처럼 (다중) 맵에 이벤트 리스너 폐쇄를 저장하고 싶습니다 :

var eventListeners = new MultiMap[EventDescriptor, (EventObject => Unit)]; 

내 질문은, 그래서이를 다시 작성하는 몇 가지 방법이 저장된 클로저의 함수 시그니처는 EventObject 또는 임의의 서브 클래스 일 수 있습니까? 다음과 같은 뭔가 :

나는 내가 리스너 함수를 정의 할 때 알려진 하위 가질 수 있도록
var eventListeners = new MultiMap[EventDescriptor, [A <: EventObject](A => Unit)] 

:

eventListeners.put(KEY_EVENT, (e:KeyEventObject) => { ... }) 
eventListeners.put(MOUSE_EVENT, (e:MouseEventObject) => { ... }) 
+1

귀하의 코드가 더 이상 사용되지 않는다고 말해야합니다.이 대화는 https://groups.google.com/forum/#!msg/scala-ide-dev/PyejSImLLtE/EbqqSKPUMisJ에서 확인해야합니다. "케이스 - 투 - 케이스 상속은 금지되어 있습니다."... – idonnie

+0

나는 그것을 알지 못했습니다. 저는 scala 2.9로 표준화되었지만 어쩌면 업그레이드 할 때가되었습니다. –

+0

@DaveRafkind : 2.9에서도 대소 문자를 구분하지 않는 것이 좋습니다. 금지되지 않지만 컴파일러 경고가 나오는 곳입니다. –

답변

2

많은 것들이 불가능하지 않습니다.당신은 타입 클래스로, 예를 들어, 다음과 같은 작업을 수행 할 수 있습니다 지금

class HMultiMap { 
    import scala.collection.mutable.{ Buffer, HashMap } 

    type Mapping[K, V] 

    private[this] val underlying = new HashMap[Any, Buffer[Any]] 

    def apply[K, V](key: K)(implicit ev: Mapping[K, V]) = 
    underlying.getOrElse(key, Buffer.empty).toList.asInstanceOf[List[V]] 

    def add[K, V](key: K)(v: V)(implicit ev: Mapping[K, V]) = { 
    underlying.getOrElseUpdate(key, Buffer.empty) += v 
    this 
    } 
} 

과 :

sealed trait EventObject 
case class KeyEventObject(c: Char) extends EventObject 
case class MouseEventObject(x: Int, y: Int) extends EventObject 

sealed trait EventDescriptor 
case object KEY_EVENT extends EventDescriptor 
case object MOUSE_EVENT extends EventDescriptor 

class EventMap extends HMultiMap { 
    class Mapping[K, V] 

    object Mapping { 
    implicit object k extends Mapping[KEY_EVENT.type, KeyEventObject => Unit] 
    implicit object m extends Mapping[MOUSE_EVENT.type, MouseEventObject => Unit] 
    } 
} 

을 조금 지저분하지만 사용이 훨씬 예뻐이다 :

val eventListeners = new EventMap 

eventListeners.add(KEY_EVENT)((e: KeyEventObject) => println(e.c)) 
eventListeners.add(MOUSE_EVENT)((e: MouseEventObject) => println("X: " + e.x)) 
eventListeners.add(KEY_EVENT)((e: KeyEventObject) => println(e.c + " again")) 

우리 개별 이벤트 핸들러를 선택할 수 있음을 확인할 수 있습니다.

scala> eventListeners(KEY_EVENT).size 
res3: Int = 2 

그리고 우리는 그것을 위해 모든 핸들러를 이벤트를 발생하고 실행하는 척 할 수 있습니다

scala> eventListeners(KEY_EVENT).foreach(_(KeyEventObject('a'))) 
a 
a again 

을 그리고 아무것도 적절한 증거없이 기본 느슨하게 형식의지도로 얻을 수 없기 때문에이 모든 완벽하게 안전합니다. 예를 들어, String에서 Unit으로 함수를 추가하려고하면 컴파일 타임 오류가 발생합니다.

+0

고마워, 이제 알았어. –

0

을 그것은 불가능합니다.

val f = eventListeners(key).head 

어떻게 기능 f을 부를 것이다 :

는의 당신은 Map 있다고 가정하자? 유형이 EventObject 인 매개 변수가 있습니까? 당신은 할 수 없습니다. KeyEventObject => Unit 일 수 있습니다. 유형이 KeyEventObject 인 매개 변수를 사용합니까? 당신은 할 수 없습니다. MouseEventObject => Unit 일 수 있습니다.

PartialFunction[EventObject, Unit]을 사용할 수 있으며 매번 isDefinedAt을 확인할 수 있지만보기에는 어렵습니다.

0

편집

불가능 보인다. 또한 케이스 - 케이스 상속은 어쩌면 일부 특정 특성을 사용하여 이벤트 객체 클래스를 밀봉하고 그 특성을 확장 선언 스칼라 - 2.10

...

금지됩니다?

val eventListeners = new MultiMap[EventDescriptor, ((_ >: EventTrait) => Unit)] 

List에서 소스 :

:+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That 

That은 -, 그것은 암시 빌더하여 EventTrait, 각 이벤트 유형에 대해 하나의 빌더에서 특정 유형의 구축 될 수있다. elem: B 대신 classOf [B]를 사용해보십시오. 다른 classOf을 사용하여 MultiMap 액세스 할 get 메소드 만들기 : 그것은

미운 적절한 형식으로 이벤트를 변환 할 수 없습니다

def getMouseEvent(ed: EventDescriptor) = multimap.entrySet.filter(
(a, b) => a == ed).map((a, b) => (a, convertTo(ClassOf[MouseEvent], b)). 
filter((a, b) => b != null) 

convertTo 경우는 null를 돌려줍니다.

+0

작동하지 않습니다 : http://stackoverflow.com/q/13828647/406435 – senia

+0

val a : 목록 [(_ _ : 옵션 [_]) => 단위)] = 목록 ((_ : Int) = > {}) - Int에서 add 함수를 허용하지 않습니다. – idonnie

+0

해당 컬렉션에서 함수를 호출 할 수있는 방법이 없습니다. 'Nothing => Unit'을 추가 할 수 있습니다. – senia

관련 문제