Общий сбор по типу в scala

В Скала 2.9.1

С

def collectFirstOfT[T](la: List[_])(implicit m:Manifest[T]) : Option[T] = {
  la.collect{case x if m.erasure.isAssignableFrom(x.getClass) => x}.
    headOption.asInstanceOf[Option[T]]}

class A
class B

почему это выражение:

val oB:Option[B] = collectFirstOf(List(new A,new B)) 

компилирует, но собирает Some(A), но

val oB =collectFirstOf[B](List(new A,new B))

работает отлично.

Как вывести T из Option[T]?


person jwinandy    schedule 12.04.2012    source источник


Ответы (3)


Вы должны смотреть на следующую строку как на две отдельные части, левую часть = и правую:

val oB: Option[B] = collectFirstOf(List(new A,new B))

Здесь вы ожидаете, что тип выражения collectFirstOf (значение r) должен выводиться из типа значения oB. Компилятор не может этого сделать. Вы должны сказать конкретно, какой тип вы ожидаете. Возьмем следующий пример:

val v: Long = 1 + 4

Тип выражения 1 + 4 — Int. Затем этот int преобразуется в Long. Компилятор не делает и не может сделать вывод, что вы хотите, чтобы 1 или 4 были длинными:

Итак, чтобы решить вашу проблему, вам нужно сообщить компилятору, какой тип вы ожидаете, иначе он предполагает java.lang.Object:

val oB = collectFirstOf[B](List(new A,new B))

Таким образом, манифест получает правильное назначение, и с миром все в порядке. Так почему же компилируется следующее:

val oB:Option[B] = collectFirstOfT(List(new A,new B))
oB: Option[B] = Some(A@10f3a9c)

на первый взгляд кажется, что это не должно работать, но работает. Это связано с тем, что collectFirstOfT на самом деле возвращает Option[Nothing], который можно безопасно преобразовать в Option[B]:

scala> val f = collectFirstOfT(List(new A,new B))
f: Option[Nothing] = Some(A@baecb8)

scala> f.asInstanceOf[Option[B]]
res4: Option[B] = Some(A@baecb8)
person Matthew Farwell    schedule 12.04.2012
comment
Хорошо поймал ! Как я могу предотвратить неправильное использование функции? (В идеале он не должен компилироваться) - person jwinandy; 12.04.2012
comment
Одним из быстрых и простых способов было бы добавить явный аргумент, класс: collectFirstOfT[T](cls: Class[T], la: List[_]), а затем вызвать как: collectFirstOfT(classOf[B], List(new А, новый Б)). Это вернет Option[B], как и ожидалось. - person Matthew Farwell; 12.04.2012

Этот:

val oB:Option[B] = collectFirstOfT(List(new A,new B)) 

Эквивалентно этому:

val oB:Option[B] = collectFirstOfT[Nothing](List(new A,new B))

Поскольку Nothing является подклассом всего, то он может быть назначен из A. Увы, его также можно назначить из B, что означает, что вы можете назначить Option[Nothing] в Option[B].

Забавный факт: это правда, потому что Option является ковариантным. Если бы это было не так, то T нужно было бы выводить как B, что заставляло бы его работать.

Забавный факт 2: этот код не компилируется на вчерашнем стволе.

person Daniel C. Sobral    schedule 12.04.2012

Поскольку компилятор не может вывести T из аргументов, вы должны написать это явно. В первом случае collect принимает весь список.

person Sergey Passichenko    schedule 12.04.2012
comment
T (или Option[T]) предназначен только для возвращаемого типа. {case x if m.erasure.isAssignableFrom(x.getClass) => x} это PartialFunction[-Any,+Any]. - person jwinandy; 12.04.2012