Scala 2.7.2以来、Javaの型消去の回避策であるManifest
と呼ばれるものがあります。しかし、Manifest
はどのように正確に機能しますか?それを使用する必要がありますか?
Jorge Ortizによるブログ投稿マニフェスト:Reified Typesはその一部を説明していますが、使用方法については説明していません context bounds と一緒に。
また、ClassManifest
とは何ですか、Manifest
との違いは何ですか?
型の消去に関していくつかの警告があるコード(大きなプログラムの一部で、ここに簡単に含めることはできません)があります。私はマニフェストを使用してこれらを解決できると思いますが、どのように正確に定かではありません。
コンパイラーは、JVMランタイムが簡単に表現できる以上の型に関する情報を知っています。マニフェストは、コンパイラが、実行時に失われた型情報に関する次元間メッセージをコードに送信する方法です。
これは、クレプトン人が化石記録と人間の「ジャンク」DNAにエンコードされたメッセージを残した方法に似ています。光速度と重力共鳴場の制限により、それらは直接通信できません。しかし、あなたが彼らの信号に合わせる方法を知っているなら、あなたは昼食に何を食べるか、どの宝くじ番号をプレーするかを決めることから、想像できない方法で利益を得ることができます。
マニフェストが、詳細を知らなくても表示されているエラーに利益をもたらすかどうかは明らかではありません。
マニフェストの一般的な使用法の1つは、コレクションの静的な型に基づいてコードの動作を変えることです。たとえば、List [String]を他のタイプのリストとは異なる方法で処理する場合はどうなりますか。
def foo[T](x: List[T])(implicit m: Manifest[T]) = {
if (m <:< manifest[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
foo(List("one", "two")) // Hey, this list is full of strings
foo(List(1, 2)) // Non-stringy list
foo(List("one", 2)) // Non-stringy list
これに対するリフレクションベースのソリューションには、おそらくリストの各要素の検査が含まれます。
コンテキストバウンドはscalaで型クラスを使用するのに最も適しているようで、Debasish Ghoshによってここで詳しく説明されています: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here -i.html
コンテキスト境界は、メソッドシグネチャをより読みやすくすることもできます。たとえば、上記の関数は、次のようなコンテキスト境界を使用して書き換えることができます。
def foo[T: Manifest](x: List[T]) = {
if (manifest[T] <:< manifest[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
完全な答えではありませんが、Manifest
とClassManifest
の違いについては、 Scala 2.8 Array
paper で例を見つけることができます。
残っている唯一の質問は、汎用配列作成の実装方法です。 Javaとは異なり、Scalaはインスタンスの作成を許可します。新しい
Array[T]
で、T
は型パラメーターです。これは、均一な配列表現が存在しないという事実を考えると、どのように実装できますか? Java?これを行う唯一の方法は、
T
型を記述する追加のランタイム情報を要求することです。 Scala 2.8には、Manifestと呼ばれる新しいメカニズムがあります。タイプManifest[T]
のオブジェクトは完全な情報を提供しますタイプT
について。Manifest
値は通常、暗黙的なパラメーターで渡されます。コンパイラーは、静的に既知の型T
に対してそれらを構築する方法を知っています。また、weaker formという名前があります
ClassManifest
型のレベルクラス。必ずしもすべての引数型を知っている必要はありません。
配列の作成に必要なのは、このタイプのランタイム情報です。
例:
ClassManifest[T]
を暗黙的なパラメーターとしてメソッドに渡すことにより、この情報を提供する必要があります。
def tabulate[T](len:Int, f:Int=>T)(implicit m:ClassManifest[T]) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
省略形として、代わりに型パラメーター
T
でコンテキストbound1を使用できます。
(これを参照してください 説明のためにSOの質問 )
、与える:
def tabulate[T: ClassManifest](len:Int, f:Int=>T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
Int
、またはString
、またはList[T]
などのタイプでtabulateを呼び出す場合、Scalaコンパイラーは、暗黙的な引数としてtabulateに渡すクラスマニフェストを作成できます。 。
マニフェストは、JVM(ジェネリックをサポートしない)上で実行するために型消去されるジェネリック型を具体化することを目的としていました。しかし、彼らにはいくつかの深刻な問題がありました。それらは単純すぎて、Scalaの型システムを完全にサポートすることができませんでした。したがって、これらはScala 2.10でdeprecatedであり、TypeTag
s(本質的にはScalaコンパイラーに置き換えられます)それ自体は型を表すために使用されるため、Scala型を完全にサポートします)。違いの詳細については、以下を参照してください。
いつ必要ですか?
2013-01-04より前、 Scala 2.10がリリースされたとき 。
manifest
in scala
ソース(Manifest.scala
)を確認してみましょう:
Manifest.scala:
def manifest[T](implicit m: Manifest[T]) = m
したがって、次のサンプルコードに関して:
def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
if (m <:< manifest[String]) {
"its a string"
} else {
"its not a string"
}
}
manifest
function
は、サンプルコードで提供したm: Manifest[T]
を満たす暗黙のtype parameter
を検索することがわかります。これはmanifest[String]
でした。そのため、次のようなものを呼び出す場合:
if (m <:< manifest[String]) {
関数で定義した現在のimplicit m
がmanifest[String]
型であり、manifest
がmanifest[T]
型の関数である場合、特定のmanifest[String]
を検索し、そのような暗黙的なものがあるかどうかを確認します。