2016-10-23 8 views
0

Я пытаюсь использовать функцию, которая использует общий тип, который передается как часть определения карты.Функция с общим типом, которые расширяют класс

Вот мой код

object EventMapping { 


    val eventMapping = collection.mutable.Map[Class[_ <: EventBase], (User, EventBase) => Unit]() 

    setMapping(classOf[UserCreated], (user, evt) => user.loadUserName(evt.userName)) 


    private def setMapping[T <: EventBase](clazz: Class[T],fn: (User, T) => Unit) { 
    eventMapping += clazz -> fn.asInstanceOf[(User, EventBase) => Unit] 
    } 

} 

класс UserCreated класс ан событие, которое простирается от EventBase, так, так как в setMapping я определяю T продленного от EventBase, и в setMapping вызова I'm определяющем, какой тип класса I Я использую в функции

user.loadUserName(evt.userName)) 

Я ожидал, что ЭВТ было бы рассматривать как событие CreatedUser, но все-таки компилятор считать EventBase.

Аналогичная база кода на Java работает, но я не знаю, что мне здесь не хватает.

Здесь класс UserCreated

class UserCreated @JsonCreator() (@JsonProperty("userName")val userName: String) extends EventBase{ 


    @JsonProperty("userName") def getUserName: String = { 
    userName 
    } 
} 

Это трассировку стека

[info] Compiling 5 Scala sources to /Development/proyectX/target/scala-2.11/classes... 
[error] /Development/proyectX/app/persistance/EventMapping.scala:11: missing parameter type 
[error] setMapping(classOf[UserCreated], (user, evt) => user.loadUserName(evt.asInstanceOf[UserCreated].userName)) 
[error]           ^
[error] one error found 
[error] (compile:compile) Compilation failed 
+0

Какая ошибка? Можете ли вы опубликовать сообщение об ошибке компилятора? – Yawar

+0

Я добавляю трассировку стека – paul

ответ

3

Вопрос заключается в том, что в определении setMapping, вы сообщаете компилятору только что T является подтипом EventBase , Поэтому, когда вы вызываете setMapping, и внутри вызова у вас есть evt.userName, компилятор не может гарантировать, что все подтипы EventBase поддерживают член userName, так что это ошибка компиляции. Поэтому, когда вы делаете evt.asInstanceOf[UserCreated].userName, вы лично гарантируете компилятору, что evt действительно является UserCreated, и он поддерживает элемент userName.

Во-вторых, ошибка missing parameter type компиляции (не трассировки стека кстати, стек следы только от выполнения исключений) является результатом алгоритма вывода типа Scala не является совершенным. Из-за причуда в алгоритме, вы можете сделать его более точным перемещением функции отображения (fn) в свой собственный список параметров:

object EventMapping { 
    val eventMapping = 
    collection.mutable.Map[Class[_ <: EventBase], (User, EventBase) => Unit]() 

    setMapping(classOf[UserCreated]) { (user, evt) => 
    user.loadUserName(evt.userName) 
    } 

    private def setMapping[T <: EventBase](
    clazz: Class[T])(fn: (User, T) => Unit): Unit = 
    eventMapping += clazz -> fn.asInstanceOf[(User, EventBase) => Unit] 
} 

Это также устраняет необходимость в потупив evt.asInstanceOf[UserCreated] потому что теперь компилятор может сделать вывод, это правильно.

Наконец, иногда тип inferencer просто не может правильно выровнять все и все еще дает вам ошибку компиляции. В этом случае вы можете просто передать аргумент типа в явном виде:

setMapping[UserCreated](classOf[UserCreated]) { (user, evt) => 
    user.loadUserName(evt.userName) 
} 

Это говорит компилятору, что везде есть общий тип T, в этом призыве заменить, что с UserCreated.

P.S. тот факт, что вы должны сбрасывать, как правило, является признаком того, что вы можете использовать более идиоматическую и сложную функцию Scala - typeclasses.

+0

Это был ключ, который я пропустил setMapping [UserCreated] Спасибо большое! – paul

Смежные вопросы