Почему это печатает wtf? Сопоставление с образцом не работает со структурными типами?
"hello" match {
case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
case _ => println("okie dokie")
}
Почему это печатает wtf? Сопоставление с образцом не работает со структурными типами?
"hello" match {
case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
case _ => println("okie dokie")
}
Запуск этого примера в интерпретаторе 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 => ...
}
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
. В идеальном мире манифест структурного типа должен быть равен манифесту любого типа, содержащего требуемые методы. По крайней мере, это ход моих мыслей. К сожалению, это не удается скомпилировать, так как компилятор вводит 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")
}
HasFoo
более гибко, например val HasFoo = new Has[{def foo():Unit}]("foo")
. Я просто пытался сделать это таким образом, но, похоже, все еще есть некоторые проблемы с более сложными типами, такими как {def foo(i:Int):Int}
.
- person Debilski; 08.08.2010
containsMethod
можно обезвожить до Try(x.getClass.getMethod(name, params: _*)).isSuccess
- person Oleg Rudenko; 08.06.2017