Почему это печатает wtf? Разве сопоставление с образцом не работает над структурными типами?
"hello" match {
case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
case _ => println("okie dokie")
}
Запуск этого примера в интерпретаторе Scala с неотмеченными предупреждениями на (scala -unchecked
) выдает следующее предупреждение: warning: уточнение AnyRef{def doesNotExist(Int,List[_]): Double} в типовом шаблоне не проверяется, так как он удаляется стиранием
. К сожалению, такой тип не может быть проверен во время исполнения, так как JVM не имеет повторно модифицированных генераторов.
Все, что JVM видит в этом паттерне совпадение:
"hello" match {
case s: Object => ...
case annon: Object => ...
}
EDIT: В ответ на ваши комментарии, я думал о решении, но не успел разместить его вчера. К сожалению, даже если оно должно работать , компилятор не смог внедрить правильный Manifest
.
Проблема, которую вы хотите решить, заключается в том, чтобы сравнить, если объект имеет заданный структурный тип. Вот код, о котором я думал (Scala 2.8-r20019, т.к. Scala 2.7.6.final пару раз обрушилась на меня при игре со схожими идеями)
type Foo = AnyRef { def doesNotExist(i: Int, x: List[_]): Double }
def getManifest[T](implicit m: Manifest[T]) = m
def isFoo[T](x: T)(implicit mt: Manifest[T]) =
mt == getManifest[Foo]
Метод isFoo
в основном сравнивает манифесты класса x
из Foo
. В идеальном мире, манифест структурного типа должен быть равен манифесту любого типа, содержащему требуемые методы. По крайней мере, это мой ход мыслей. К сожалению, при вызове getManifest[Foo]
компиляция не удается, так как компилятор при вызове getManifest[Foo]
инжектирует Manifest[AnyRef]
вместо Manifest[Foo]
. Интересно, что если не использовать структурный тип (например, тип Foo = String
), то этот код компилируется и работает так, как ожидается. В какой-то момент я задам вопрос, почему это не работает со структурными типами - это проектное решение, или это просто проблема экспериментального рефлексивного API.
В противном случае, всегда можно воспользоваться рефлексией Java, чтобы посмотреть, содержит ли объект метод.
def containsMethod(x: AnyRef, name: String, params: java.lang.Class[_]*) = {
try {
x.getClass.getMethod(name, params: _*)
true
}
catch {
case _ => false
}
}
который работает, как ожидается:
containsMethod("foo", "concat", classOf[String]) // true
containsMethod("foo", "bar", classOf[List[Int]]) // false
... но это не очень хорошо.
Также, обратите внимание, что структура структурного типа недоступна во время исполнения. Если у вас есть метод def foo(x: {def foo: Int}) = x.foo
, то после стирания вы получаете def foo(x: Object) = [некоторое отражение, вызывающее foo на x]
, информация о типе теряется. Поэтому отражение используется в первую очередь, так как вы должны вызвать метод на Object
и JVM не знает, имеет ли Object
такой метод.
Если вам нужно использовать отражение, вы можете хотя бы сделать его лучше с помощью экстрактора:
object WithFoo {
def foo(){
println("foo was called")
}
}
object HasFoo {
def containsMethod(x: AnyRef, name: String, params: Array[java.lang.Class[_]]) : Boolean = {
try {
x.getClass.getMethod(name, params: _*)
true
} catch {
case _ => false
}
}
def unapply(foo:AnyRef):Option[{def foo():Unit}] = {
if (containsMethod(foo, "foo", new Array[Class[_]](0))) {
Some(foo.asInstanceOf[{def foo():Unit}])
} else None
}
}
WithFoo.asInstanceOf[AnyRef] match {
case HasFoo(foo) => foo.foo()
case _ => println("no foo")
}