Да, вы оказались в стране стирания.
В 1-м случае (исходный код), компилятор знает, что A
является Foo
, но во время выполнения параметр типа стирается с верхним типом связанного, который Base
в этом примере (если не указать верхний тип, параметр типа стирается до Object
). Так JVM видит свой код следующим образом (не замечают нет типа параметризации):
def checker(func: Base => String): Base => String =
(b: Base) => b match {
case a : Base => func(a)
case _ => "error"
}
Любой Foo
или Bar
объект будет соответствовать Base
, а затем JVM попытается бросить его Foo
и вызвать func
. Работает, если b
является объектом класса Foo
, но выбрасывает исключение для Bar
. Без сюрпризов.
Во втором случае вы удаляете параметр типа и заменяете A
на Foo
в определении checker
. Так что ваш код во время выполнения выглядит следующим образом (функция не типа параметризованных начать с, так что ничего не стирается):
def checker(func: Foo => String): Base => String =
(b: Base) => b match {
case a: Foo => func(a)
case _ => "error"
}
Это работает, как ожидалось, но это фиксируется на проверку только для Foo
.Поэтому вам нужно написать отдельный checker
для Bar
и любого другого типа, который вы хотите проверить.
В третьем случае вы сохраняете параметр типа, но опускаете аргумент функции и заменяете func(a)
на "good"
. Это приводит к аналогичному коду к случаю 1 (опять стирание от Foo
до Base
), за исключением того, что func
не вызывается, поэтому приведение к Foo
не требуется. Следовательно, никакого исключения.
def checker: Base => String =
(b: Base) => b match {
case a: Base => "good"
case _ => "error"
}
Просто любой объект, вы передаете (подкласс Base) сопоставляется с первым пунктом и checker
всегда возвращает "хорошо".
Теперь решение. Вы были на правильном пути с Manifest
(но код, который вы показали, слишком сложный для того, чего вы пытаетесь достичь). Когда вы запрашиваете Manifest
, компилятор Scala передает дополнительный объект методу/функции, который можно использовать во время выполнения для «восстановления» стираемых типов. Ниже я использую «context bound» вместо того, чтобы указывать его как (implicit manifest : Manifest[A])
, но это то же самое, короче.
def checker[A <: Base: Manifest](func: A => String): Base => String =
(b: Base) => if(manifest[A].erasure == b.getClass) func(b.asInstanceOf[A])
else "error"
Итак, когда вы называете это так:
def fooFunc(f: Foo) = "It's a foo"
def barFunc(f: Bar) = "It's a bar"
def main(arg: Array[String]) {
val check1 = checker(fooFunc)
val check2 = checker(barFunc)
println(check1(new Foo) + ", " + check1(new Bar))
println(check2(new Foo) + ", " + check2(new Bar))
}
Вы получите вывод, как и ожидалось:
It's a foo, error
error, It's a bar
Erasure является источником всех видов развлечений в Scala поскольку параметризация типа здесь более распространена, чем в Java. Ни в коем случае, я рекомендую изучить все, что вы можете.
Нет, это не поможет. Он удаляет исключение, но все же печатает «хорошо» для Foo и Bar. Проблема заключается в том, что проверка забывает, какой тип A был. –
Извините? См. Обновленный ответ, наиболее точно печатает Foo & Bar. Не уверен, что вы пытаетесь сделать, должен обновить свой вопрос с помощью кода, который вы используете в настоящее время, поэтому я и другие узнаю, о чем вы говорите (например, «все еще печатает« хорошо »?) – virtualeyes
Жаль, что вы не поняли , Цель проверки - исключить все типы, кроме одного. Если результатом является «Foo, Bar», это означает, что оба аргумента Foo и Bar имеют пиво, распознанное как A. То, что я хочу увидеть, это «Foo, error». –