2010-01-01 7 views

ответ

18

Запуск примера в интерпретаторе Scala с непроверенным предупреждения о (scala -unchecked) производит следующее предупреждение: warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure. К сожалению, общий тип, подобный этому, не может быть проверен во время выполнения, поскольку 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.

+0

Спасибо, Flaviu. Это отвечает на мой вопрос. Но мне все еще остается интересно, какой лучший способ достичь этого будет, потому что структура - это то, что действительно доступно во время выполнения через отражение. Это просто неуклюже. –

+0

Спасибо за продолжение. Очень интересный материал. –

7

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

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") 
} 
+0

Было бы неплохо, если бы он смог более гибко определить 'HasFoo', например' val HasFoo = new Has [{def foo(): Unit}] ("foo") '.Я просто пытался сделать это таким образом, но все же есть некоторые проблемы, связанные с более сложными типами, такими как '{def foo (i: Int): Int}'. – Debilski

+0

Почему компилятор не делает это автоматически? – Gabriel

+0

'containsMethod' может быть обезвожен до' Try (x.getClass.getMethod (name, params: _ *)). IsSuccess' –