我有一个带有以下签名的函数:
myFunc[T <: AnyRef](arg: T)(implicit m: Manifest[T]) = ???
如果在编译时我不知道参数的确切类型,我怎么能调用这个函数?
例如:
val obj: AnyRef = new Foo() // At compile time obj is defined as AnyRef,
val objClass = obj.getClass // At runtime I can figure out that it is actually Foo
// Now I would need to call `myFunc[Foo](obj.asInstanceOf[Foo])`,
// but how would I do it without putting [Foo] in the square braces?
我想写一些逻辑上相似的东西:
myFunc[objClass](obj.asInstanceOf[objClass])
谢谢!
更新
问题无效 - 正如@DaoWen,@ Jermo和@itsbruce正确指出的那样,我试图做的事情完全是胡说八道!我只是严厉地推翻了这个问题。 感谢你们!太糟糕了,我不能接受所有答案都是正确的:)
所以,问题是由以下情况引起的:
我正在使用Salat库将对象序列化为BSON / JSON表示形式。
Salat
有一个Grater[T]
类,用于序列化和反序列化。
从BSON调用反序列化的方法看起来是这样的:
val foo = grater[Foo].asObject(bson)
这里,类型参数的作用是明确的。我试图做的是使用相同的Grater来序列化来自我的域模型的任何实体。所以我写道:
val json = grater[???].toCompactJSON(obj)
我立即赶紧反思,只是没有看到表面上有明显的解决方案。这是:
grater[Entity].toCompactJSON(obj) // where Entity...
@Salat trait Entity // is a root of the domain model hierarchy
有时事情比我们认为的要容易得多! :)
答案 0 :(得分:5)
看来,当我写这个答案时,问题的作者意识到他不需要在运行时解析Manifest
。但是,在我看来,当我编写Yaml [de]序列化库时,我成功解决了完全合法的问题,所以我在这里留下答案。
可以使用ClassTag
甚至TypeTag
来执行您想要的操作。我不知道有关Manifest
的问题,因为该API已弃用且我没有使用它,但我相信,由于它们不像新的Scala反射那样复杂,因此更容易显示。仅供参考,Manifest
的继任者是TypeTag
。
假设您具有以下功能:
def useClasstag[T: ClassTag](obj: T) = ...
def useTypetag[T: TypeTag](obj: T) = ...
然后您需要以obj: AnyRef
作为参数进行调用,同时为ClassTag
类提供TypeTag
或obj.getClass
作为隐式参数。
ClassTag
是最简单的。您可以直接从ClassTag
实例创建Class[_]
:
useClasstag(obj)(ClassTag(obj.getClass))
就是这样。
TypeTag
更难。您需要使用Scala反射从对象中获取一个,然后您必须使用Scala反射的一些内部。
import scala.reflect.runtime.universe._
import scala.reflect.api
import api.{Universe, TypeCreator}
// Obtain runtime mirror for the class' classloader
val rm = runtimeMirror(obj.getClass.getClassLoader)
// Obtain instance mirror for obj
val im = rm.reflect(obj)
// Get obj's symbol object
val sym = im.symbol
// Get symbol's type signature - that's what you really want!
val tpe = sym.typeSignature
// Now the black magic begins: we create TypeTag manually
// First, make so-called type creator for the type we have just obtained
val tc = new TypeCreator {
def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
if (m eq rm) tpe.asInstanceOf[U # Type]
else throw new IllegalArgumentException(s"Type tag defined in $rm cannot be migrated to other mirrors.")
}
// Next, create a TypeTag using runtime mirror and type creator
val tt = TypeTag[AnyRef](rm, tc)
// Call our method
useTypetag(obj)(tt)
如您所见,这种机器相当复杂。这意味着只有在真正需要它时才应该使用它,并且正如其他人所说的那样,当你真正需要它时的情况非常罕见。
答案 1 :(得分:3)
这不起作用。以这种方式思考:你要求编译器为一个在运行时才知道的类创建一个类Manifest(在编译时!)。
但是,我觉得你以错误的方式接近问题。 AnyRef
在编译时对Foo
的类型的了解真的最多吗?如果是这样的话,你怎么能用它做任何有用的事情? (除了为AnyRef
定义的少数方法之外,您将无法调用任何方法。)
答案 2 :(得分:2)
目前尚不清楚您想要实现的目标,而且更多的背景可能会有所帮助。无论如何,这是我的2美分。
使用Manifest在这里没有帮助,因为类型参数需要在编译时知道。我的建议是这样的:
def myFunc[T](arg: AnyRef, klass: Class[T]) = {
val obj: T = klass.cast(arg)
//do something with obj... but what?
}
你可以这样称呼它:
myFunc(obj, Foo.class)
请注意,我不知道如何在myFunc
内做一些有用的事情。在编译时,除了AnyRef可用的方法之外,您不能在类型T的对象上调用任何方法。如果你想使用反射来操纵myFunc
的参数,那么就没有必要将它转换为特定的类型。
答案 3 :(得分:0)
这是使用类型安全的OO语言的错误方法。如果你需要这样做,你的设计是错误的。
myFunc[T <: AnyRef](arg: T)(implicit m: Manifest[T]) = ???
当然,这可能是无用的,正如您可能已经发现的那样。你可以在一个可能任何的对象上调用什么样的有意义的函数?您不能直接引用其属性或方法。
我想写一些逻辑上相似的东西:
myFunc[objClass](obj.asInstanceOf[objClass])
为什么呢?这种事情通常只对非常专业的案件才有必要。您是在编写一个使用依赖注入的框架吗?如果您没有对Scala的功能进行一些高度技术性的扩展,那么这不是必需的。
我打赌你知道 更多关于这个课程的内容,因为你说你不知道完全类型。基于类的OO工作方式的一个重要部分是,如果要对一般类型的对象(包括其所有子类型)执行某些操作,则将该行为放入属于该类的方法中。如果需要,让子类覆盖它。
坦率地说,你正在尝试的方法是在你对类型有足够了解的上下文中调用函数。