Как работает Scala (2.8) Manifest?
У меня есть некоторый код Scala, который довольно интенсивно использует дженерики, и я почерпнул из документов, что использование манифеста в ограничениях параметризации может помочь мне обойти проблемы стирания типов (например, я хочу создать новый объект универсального типа)). Только я хотел бы больше узнать о том, как это работает. Это почти похоже на что-то вроде хэш-карты, которая получает запись для каждого сайта вызова... Может кто-нибудь здесь уточнить?
class Image[T <: Pixel[T] : Manifest](fun() => T, size: Array[Int], data: Array[T]) {
def this(fun: () => T, size: Array[T]) {
this(fun, size, new Array[T](size(0) * size(1));
}
}
Это то, что, по-видимому, не отражено ни в одной из документов, которые я нашел на сайте, и в Google я в основном получаю старые посты, которые имеют совершенно другой синтаксис, и, поскольку в версии 2.8, похоже, многое изменилось, я Я не уверен, что они все еще точны.
2 ответа
Прошло некоторое время с тех пор, как я копался в исходном коде Scala в поисках ответа на тот же вопрос... но короткий ответ, насколько я помню, -
Manifest - это чит-код, позволяющий компилятору обходить стирание типов (он не используется во время выполнения). Это приводит к тому, что во время компиляции генерируется несколько путей кода для возможных типов ввода, соответствующих манифесту.
Манифест разрешается неявно, но если во время компиляции возникает неопределенность относительно типа Манифеста, компилятор останавливается.
С копией Manifest
у вас есть несколько вещей. Главное, что вы обычно хотите, это либо java.lang.Class
это было стерто с помощью erasure
:
class BoundedManifest[T <: Any : Manifest](value: T) {
val m = manifest[T]
m.erasure.toString match {
case "class java.lang.String" => println("String")
case "double" | "int" => println("Numeric value.")
case x => println("WTF is a '%s'?".format(x))
}
}
class ImplicitManifest[T <: Any](value: T)(implicit m: Manifest[T]) {
m.erasure.toString match {
case "class java.lang.String" => println("String")
case "double" | "int" => println("Numeric value.")
case x => println("WTF is a '%s'?".format(x))
}
}
new BoundedManifest("Foo Bar!")
// String
new BoundedManifest(5)
// Numeric value.
new BoundedManifest(5.2)
// Numeric value.
new BoundedManifest(BigDecimal("8.62234525"))
// WTF is a 'class scala.math.BigDecimal'?
new ImplicitManifest("Foo Bar!")
// String
new ImplicitManifest(5)
// Numeric value.
new ImplicitManifest(5.2)
// Numeric value.
new ImplicitManifest(BigDecimal("8.62234525"))
// WTF is a 'class scala.math.BigDecimal'?
Это довольно странный пример, но он показывает, что происходит. Я запустил это и для вывода FWIW на Scala 2.8.
[T ... : Manifest]
Граница является новым в Scala 2.8... вы привыкли неявно захватывать манифест, как показано в ImplicitManifest
, Вы на самом деле не получаете копию манифеста. Но вы можете получить один внутри вашего кода, сказав val m = manifest[T]
... manifest[_]
определяется на Predef
и демонстративно найдет правильный тип манифеста внутри ограниченного блока.
Два других основных предмета, которые вы получаете от Manifest
является <:<
а также >:>
какой тестовый подтип / супертип одного манифеста против другого. Если я правильно помню, они ОЧЕНЬ наивны с точки зрения реализации и не всегда совпадают, но у меня есть куча производственного кода, использующего их для проверки нескольких возможных стертых входных данных. Простой пример проверки по другому манифесту:
class BoundedManifestCheck[T <: Any : Manifest](value: T) {
val m = manifest[T]
if (m <:< manifest[AnyVal]) {
println("AnyVal (primitive)")
} else if (m <:< manifest[AnyRef]) {
println("AnyRef")
} else {
println("Not sure what the base type of manifest '%s' is.".format(m.erasure))
}
}
new BoundedManifestCheck("Foo Bar!")
// AnyRef
new BoundedManifestCheck(5)
// AnyVal (primitive)
new BoundedManifestCheck(5.2)
// AnyVal (primitive)
new BoundedManifestCheck(BigDecimal("8.62234525"))
// AnyRef
У Хорхе Ортиса есть отличный пост в блоге (хотя и старый) об этом: http://www.scala-blogs.org/2008/10/manifests-reified-types.html
РЕДАКТИРОВАТЬ:
Вы действительно можете увидеть, что делает Scala, попросив распечатать результаты этапа компиляции erasure.
Запуск, на моем последнем примере выше scala -Xprint:erasure test.scala
дает следующий результат:
final class Main extends java.lang.Object with ScalaObject {
def this(): object Main = {
Main.super.this();
()
};
def main(argv: Array[java.lang.String]): Unit = {
val args: Array[java.lang.String] = argv;
{
final class $anon extends java.lang.Object {
def this(): anonymous class $anon = {
$anon.super.this();
()
};
class BoundedManifestCheck extends java.lang.Object with ScalaObject {
<paramaccessor> private[this] val value: java.lang.Object = _;
implicit <paramaccessor> private[this] val evidence$1: scala.reflect.Manifest = _;
def this($outer: anonymous class $anon, value: java.lang.Object, evidence$1: scala.reflect.Manifest): BoundedManifestCheck = {
BoundedManifestCheck.super.this();
()
};
private[this] val m: scala.reflect.Manifest = scala.this.Predef.manifest(BoundedManifestCheck.this.evidence$1);
<stable> <accessor> def m(): scala.reflect.Manifest = BoundedManifestCheck.this.m;
if (BoundedManifestCheck.this.m().<:<(scala.this.Predef.manifest(reflect.this.Manifest.AnyVal())))
scala.this.Predef.println("AnyVal (primitive)")
else
if (BoundedManifestCheck.this.m().<:<(scala.this.Predef.manifest(reflect.this.Manifest.Object())))
scala.this.Predef.println("AnyRef")
else
scala.this.Predef.println(scala.this.Predef.augmentString("Not sure what the base type of manifest '%s' is.").format(scala.this.Predef.genericWrapArray(Array[java.lang.Object]{BoundedManifestCheck.this.m().erasure()})));
protected <synthetic> <paramaccessor> val $outer: anonymous class $anon = _;
<synthetic> <stable> def Main$$anon$BoundedManifestCheck$$$outer(): anonymous class $anon = BoundedManifestCheck.this.$outer
};
new BoundedManifestCheck($anon.this, "Foo Bar!", reflect.this.Manifest.classType(classOf[java.lang.String]));
new BoundedManifestCheck($anon.this, scala.Int.box(5), reflect.this.Manifest.Int());
new BoundedManifestCheck($anon.this, scala.Double.box(5.2), reflect.this.Manifest.Double());
new BoundedManifestCheck($anon.this, scala.package.BigDecimal().apply("8.62234525"), reflect.this.Manifest.classType(classOf[scala.math.BigDecimal]))
};
{
new anonymous class $anon();
()
}
}
}
}
"Связанный контекст" T ... : Manifest
является синтаксическим сахаром для неявного аргумента: (implicit man: Manifest[T])
, Таким образом, в точке создания конструктора типа, указанного class Image
компилятор находит / предоставляет манифест для фактического типа, используемого для параметра типа T
и это значение "привязывается" к полученному экземпляру класса на протяжении всего его существования и "привязывает" каждый конкретный экземпляр Image[Something]
в Манифест для его T
,