4

次のようなジェネリック クラスを作成しようとしています。

class A[T](v: Option[T]) {
  def this(v: T) = this(Some(v))
  def this() = this(None)
  def getV = v 
}

次に、いくつかのテストを行います。

scala> new A getV
res21: Option[Nothing] = None
scala> new A(8) getV
res22: Option[Int] = Some(8)

ここまでは順調ですね。しかし、メイン コンストラクターを呼び出そうとするとすぐに、次のようになります。

scala> new A(Some(8)) getV
<console>:9: error: ambiguous reference to overloaded definition,
both constructor A in class A of type (v: T)A[T]
and  constructor A in class A of type (v: Option[T])A[T]
match argument types (Some[Int])
       new A(Some(8)) getV
       ^

scala> new A(None) getV
<console>:9: error: ambiguous reference to overloaded definition,
both constructor A in class A of type (v: T)A[T]
and  constructor A in class A of type (v: Option[T])A[T]
match argument types (None.type)
       new A(None) getV
       ^

これら 2 つのコンストラクターの間で「あいまい」な点は何ですか? それとも (推測させてください) Scala の型システムについて私が知らないことはもう 1 つありますか? :)

確かに、非ジェネリック クラスを使用すると、すべてが期待どおりに機能します。私Bのクラスは問題なく動作します:

class B(v: Option[String]) {
  def this(v: String) = this(Some(v))
  def this() = this(None)
  def getV = v 
}

scala> new B("ABC") getV
res26: Option[String] = Some(ABC)
scala> new B getV
res27: Option[String] = None
scala> new B(Some("ABC")) getV
res28: Option[String] = Some(ABC)
scala> new B(None) getV
res29: Option[String] = None
4

3 に答える 3

7

は次のnew A(Some(8))いずれかです。

  • A[Int]一次コンストラクター経由の新しいインスタンス、
  • A[Option[Int]]代替コンストラクター経由の新しいインスタンス。

のように型を明示的に指定できますnew A[Int](Some(8))

于 2011-07-22T11:34:05.553 に答える
1

問題はすでに特定されています。タイピングを必要としないソリューションはどうですか?

解決策: 優先度のある暗黙の変換。

暗黙的な変換の問題は、おそらく暗黙的な def everything_is_optional[A](a: A) = Some(a) を書きたくないということです。これは、オプションの型システムを壊すためです (気付かずに昇格するという点で) . あなたはこれを望んでいるかもしれませんが、個人的には、何かがオプションであるかどうかについて混乱したときに型システムが教えてくれるのが好きです。したがって、何らかの別のラッパーが必要です。そのようです:

// Might want to do this for more than just one class, so generalize
class Implicator[A,B](val value: A) {
  def to[C] = this.asInstanceOf[Implicator[A,C]]
}

class X[A](i: Implicator[Option[A],X[A]]) {
  private val v = i.value
  def getV = v
}
trait LowPriorityX {
  implicit def everything_for_x[A](a: A) = new Implicator(Option(a)).to[X[A]]
}
object X extends LowPriorityX {
  implicit def option_for_x[A](oa: Option[A]) = new Implicator(oa).to[X[A]]
}

これで、これを試すことができます ( :pasteREPL を使用する場合は、モードで上記を入力するか、オブジェクト内にすべて入力してオブジェクトをインポートしobject Xclass Xください。

scala> new X(5)
res0: X[Int] = X@26473f4c

scala> new X(Some(5))
res1: X[Int] = X@1d944379

したがって、少し余分なコードと暗黙の変換を犠牲にして、目的の動作を取得します。

動作する型エンコーディングスキームがあることはほぼ確実ですが、それを完了する時間がありませんでした。さらに、コンパイラが型境界に使用される暗黙的なものを作成してボックス化することを主張していることに気付いた後、熱意を失いましたそのようなスキームでは、型チェックにのみ必要ですが。

于 2011-07-22T19:42:01.247 に答える
0

ジェネリック クラスに複数の構造が必要な場合の 2 つの回避策。

1)興味のあるコンストラクターを持つ別のクラスでクラスを拡張します。+inに注意してください。これは、が と共変C[+T]であることを意味するため、が必要なときに受け入れられます。少なくともほとんどの場合、この共分散のことを確認してください。C0[T]C[+T]C0[T]C[T]

class C[+T](i: Int)

class C0[T](s:String) extends C[T](Integer.parseInt(s))

2) たとえば、コンパニオン オブジェクトに便利に配置できるメソッドを使用します。Scalaではかなり慣用的です。

object C {
  def apply[T](s:String) = new C[T](Integer.parseInt(s))
}
于 2019-09-06T19:41:18.103 に答える