1

抽象型フィールドを持つ抽象クラスがあるとします。

abstract class A {type T}

ここで、 type のオブジェクトを返すメソッドがあるとしますAが、 type フィールドTは異なる場合があります。これらのオブジェクトをどのように区別できますか?

パターンマッチを試すことができます:

object Test {
  def tryToDistinguish(a: A) = 
    a match {
      case b: A {type T = String} => println("String type")
      case b: A {type T = Int} => println("Int type")
      case b: A => println("Other type")
  }
}

しかし、コンパイラは文句を言うでしょう:

$>scalac -unchecked Test.scala
Test.scala:8: warning: refinement example.test.A{type T = String} in type patter
n example.test.A{type T = String} is unchecked since it is eliminated by erasure

      case b: A {type T = String} => println("String type")
          ^
Test.scala:9: warning: refinement example.test.A{type T = Int} in type pattern e
xample.test.A{type T = Int} is unchecked since it is eliminated by erasure
          case b: A {type T = Int} => println("Int type")
              ^
two warnings found

型フィールドの型は消去によって削除されるようです (副次的な質問: Java では型フィールドがパラメーター型に変換されるためですか?)

したがって、これは機能しません。

scala> Test.tryToDistinguish(new A {type T = Int})
String type

代替 :Aオブジェクトを区別するために、代わりに列挙を作成し、クラスに追加のフィールドを配置することができます。しかし、これは、型システムを再実装することを意味するため、においがします。

質問:タイプ フィールドを使用してオブジェクトのタイプを区別する方法はありますか? そうでない場合、良い回避策は何ですか?

4

3 に答える 3

2

ご想像のとおり、抽象型メンバーは、型の消去中に消去されるコンパイル時の情報です。パラメータを使用する回避策を次に示しimplicitます。ディスパッチは静的です。

scala> class A {
     |   type T
     | }
defined class A

scala> implicit def distString(a: A { type T = String }) = "String"
distString: (a: A{type T = String})java.lang.String

scala> implicit def distInt(a: A { type T = Int }) = "Int"
distInt: (a: A{type T = Int})java.lang.String

scala> implicit def distOther[O](a: A { type T = O }) = "Other"
distOther: [O](a: A{type T = O})java.lang.String

scala> def distinguish(a: A)(implicit ev: A { type T = a.T } => String) = ev(a)
distinguish: (a: A)(implicit ev: A{type T = a.T} => String)String

scala> distinguish(new A { type T = String })
res2: String = String

scala> distinguish(new A { type T = Int })
res3: String = Int

scala> distinguish(new A { type T = Float })
res4: String = Other 

別の方法:

scala> def dist(a: A)(implicit s: a.T =:= String = null, i: a.T =:= Int = null) =
     |   if (s != null) "String" else if (i != null) "Int" else "Other"
dist: (a: A)(implicit s: =:=[a.T,String], implicit i: =:=[a.T,Int])String

scala> dist(new A { type T = String })
res5: String = String

scala> dist(new A { type T = Int })
res6: String = Int

scala> dist(new A { type T = Float })
res7: String = Other

編集:

上記の解決策では満足できず、実行時にこの型情報を具体化して内省したい場合は、Manifests と呼ばれるものを使用して同様に行うことができます。

scala> :paste
// Entering paste mode (ctrl-D to finish)

abstract class A {
  type T
  def man: Manifest[T]
}

object A {
  def apply[X](implicit m: Manifest[X]) = new A { type T = X; def man = m }
}


// Exiting paste mode, now interpreting.

defined class A
defined module A

scala> def disti(a: A): String = a match {
     |   case _ if a.man <:< manifest[String] => "String"
     |   case _ if a.man <:< manifest[Int] => "Int"
     |   case _ => "Other"
     | }
disti: (a: A)String

scala> disti(A.apply[String])
res14: String = String

scala> disti(A.apply[Int])
res15: String = Int

scala> disti(A.apply[Float])
res16: String = Other
于 2012-07-03T12:50:18.847 に答える
2

<%<2 つの型に互換性があるかどうかをコンパイル時に通知する which を使用できます。

scala> class A { type T }
defined class A

scala> implicitly[A { type T=String } <%< A { type T=Int } ]
<console>:9: error: could not find implicit value for parameter e: <%<[A{type T = String},A{type T = Int}]
              implicitly[A { type T=String } <%< A { type T=Int } ]
                        ^

scala> implicitly[A { type T=String } <%< A { type T=String } ]
res1: <%<[A{type T = String},A{type T = String}] = <function1>

scala> implicitly[A { type T=String } <%< A ]
res3: <%<[A{type T = String},A] = <function1>
于 2012-07-03T12:13:22.810 に答える
1

副次的な質問: Java では型フィールドがパラメーター型に変換されるためですか?

バイトコードには型パラメーターがないため、型フィールドをそれらに変換できない可能性があります。タイプ フィールドは、コンパイル中にのみ存在します。

代替 : オブジェクトを区別するために、代わりに列挙を作成し、クラス A に追加のフィールドを配置することができます。しかし、これは、型システムを再実装することを意味するため、においがします。

実行時に作成できるタイプの質問は 1 種類だけです。これはどのクラスですか? Scala 2.10 ではもう少し多くの情報が得られますが、やはりその質問に行き着きます。

実際に をサブクラス化Aすると、リフレクションを通じてその情報を取得できます (2.10)。そうでなければ、私はそうは思いません - 私は REPL でテストしましたが、そこでは動作しませんが、クラスファイルにコンパイルされたものには、それらに関するより多くの情報があります。

他の質問は、値について行う必要があります。

質問: タイプ フィールドを使用してオブジェクトのタイプを区別する方法はありますか? そうでない場合、良い回避策は何ですか?

いいえ、Scala 2.10、リフレクション、およびサブクラスを除きます。良い回避策?値を使用します。ただし、それを処理する通常の方法は列挙ではなく、Manifest暗黙的に生成できる です。

Scala で型消去を回避する方法についての質問を調べてください。

于 2012-07-03T16:15:01.067 に答える