Образцы, соответствующие структурным типам в Scala
Почему это печать WTF? Сопоставление с образцом не работает на структурных типах?
"hello" match {
case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
case _ => println("okie dokie")
}
2 ответа
Запуск этого примера в интерпретаторе Scala с непроверенными предупреждениями (scala -unchecked
) выдает следующее предупреждение: warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure
, К сожалению, универсальный тип, подобный этому, не может быть проверен во время выполнения, поскольку JVM не имеет усовершенствованных универсальных шаблонов.
Все, что JVM видит в этом сопоставлении с образцом:
"hello" match {
case s: Object => ...
case annon: Object => ...
}
РЕДАКТИРОВАТЬ: В ответ на ваши комментарии, я думал о решении, но не было времени, чтобы опубликовать его вчера. К сожалению, даже если это должно работать, компилятор не может ввести правильное 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
, В идеальном мире манифест структурного типа должен быть равен манифесту любого типа, содержащего требуемые методы. По крайней мере, это мой ход мыслей. К сожалению это не компилируется, так как компилятор вводит Manifest[AnyRef]
вместо Manifest[Foo]
при звонке getManifest[Foo]
, Интересно, что если вы не используете структурный тип (например, type 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) = [some reflection invoking foo on 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")
}