Да, вы оказались в стране стирания.
В 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
всегда возвращает «good».
Теперьрешение.Вы были на правильном пути с Manifest
(но код, который вы показали, слишком сложен для того, чего вы пытаетесь достичь).Когда вы запрашиваете Manifest
, компилятор Scala передает дополнительный объект методу / функции, который можно использовать во время выполнения для «восстановления» стертых типов.Ниже я использую «привязку к контексту» вместо того, чтобы обозначать это как (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.Нет пути, я рекомендую узнать все об этом, вы можете.