Сопоставление с образцом структурные типы в Scala

Почему это печатает wtf? Разве сопоставление с образцом не работает над структурными типами?

  "hello" match {
    case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
    case _ => println("okie dokie")
  }
21
задан Mitch Blevins 1 January 2010 в 08:26
поделиться

2 ответа

Запуск этого примера в интерпретаторе 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 такой метод.

.
19
ответ дан 29 November 2019 в 21:32
поделиться

Если вам нужно использовать отражение, вы можете хотя бы сделать его лучше с помощью экстрактора:

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")
}
9
ответ дан 29 November 2019 в 21:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: