7

実装で上限を変更したときに、この奇妙な動作に遭遇しましたが、インターフェイスで変更するのを忘れていました。最後のステートメントはコンパイルすべきではないと思いますが、コンパイルして予期しない結果を返します。

trait SuperBase
trait Base extends SuperBase

class SuperBaseImpl extends SuperBase

trait Service {
  def doWork[T <: Base : Manifest](body: T => Unit): String
  def print[T <: Base : Manifest]: String
}

object ServiceImpl extends Service {
  override def doWork[T <: SuperBase : Manifest](body: T => Unit): String =
    print[T]
  def print[T <: SuperBase : Manifest]: String =
    manifest[T].runtimeClass.toString
}

val s: Service = ServiceImpl

// does not compile as expected
// s.print[SuperBaseImpl]

// returns "interface Base"
s.doWork { x: SuperBaseImpl => () }

編集

@som-snytt が-Xprint:typerオプションで言及したように、コンパイラが実際に何を推論するかを確認できます。

s.doWork[Base with SuperBaseImpl]

これは、「インターフェイス ベース」を取得する理由を説明しています。しかし、この場合、型推論がどのように、なぜ機能するのか、まだよくわかりません。

4

3 に答える 3

3

を使用-Xprint:typerすると、コンパイラが何を推論するかがわかりますT

s.doWork[Base with SuperBaseImpl]

バウンドは何を表現しようとしているのか? 関数はパラメーターで共変であるためbody、十分に狭い型の特定の引数を受け入れる必要があることを表現しています。通常、関数はワイド型を処理する必要があります。

下限を意図していたのかもしれません。

scala> trait SuperBase
defined trait SuperBase

scala> trait Base extends SuperBase
defined trait Base

scala> class SuperBaseImpl extends SuperBase
defined class SuperBaseImpl

scala> trait Service { def f[A >: Base : Manifest](g: A => Unit): String }
defined trait Service

scala> object Impl extends Service { def f[A >: Base : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
defined object Impl

scala> (Impl: Service).f { x: Base => () }
res0: String = interface Base

scala> (Impl: Service).f { x: SuperBase => () }
res1: String = interface SuperBase

scala> (Impl: Service).f { x: SuperBaseImpl => () }
<console>:17: error: inferred type arguments [SuperBaseImpl] do not conform to method f's type parameter bounds [A >: Base]
       (Impl: Service).f { x: SuperBaseImpl => () }
                       ^
<console>:17: error: type mismatch;
 found   : SuperBaseImpl => Unit
 required: A => Unit
       (Impl: Service).f { x: SuperBaseImpl => () }
                                            ^
<console>:17: error: No Manifest available for A.
       (Impl: Service).f { x: SuperBaseImpl => () }
                         ^

scala> object Impl extends Service { def f[A >: SuperBase : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
<console>:14: error: overriding method f in trait Service of type [A >: Base](g: A => Unit)(implicit evidence$1: Manifest[A])String;
 method f has incompatible type
       object Impl extends Service { def f[A >: SuperBase : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
                                         ^
于 2015-11-13T06:46:57.613 に答える
1

あなたのコードが言っていることに注意してください:

メソッド ServeImp.doWork は、「Base および Superbase のサブクラスであるクラス T を受け入れなければならない関数」であるパラメーターを受け入れなければなりません (MUST)。

SuperBaseImpl は Base のサブクラスではありませんが、その要件を満たす「SuperBaseImpl を Base で拡張する」クラス X が存在する可能性があるため、エラーではありません。

型推論が発生すると、T は上記のすべての要件を満たす「foo.Base with foo.SuperBaseImpl」に解決されます。実行時に JVM でその型を記述する方法がないため、runtimeClass はインターフェース Base ですが、manifest.toString を実行すると、正しい型が表示されます。

あなたの例でそれを実証する実際の方法はありませんが、次のことを考慮してください。

trait SuperBase
trait Base extends SuperBase

class SuperBaseImpl(val a: String) extends SuperBase

trait Service {
  def doWork[T <: Base : Manifest](body: T => String): (T) => String
}

object ServiceImpl extends Service {
  override def doWork[T <: SuperBase : Manifest](body: T => String): (T) => String =
    x => "Manifest is '%s', body returned '%s'".format(manifest[T].toString(), body(x))
}

val s: Service = ServiceImpl

val f = s.doWork { x: SuperBaseImpl => x.a }
// f: Base with SuperBaseImpl => String = <function1>

f(new SuperBaseImpl("foo") with Base)
// res0: String = Manifest is 'Base with SuperBaseImpl', body returned 'foo'

f(new SuperBaseImpl("foo"))
// compile error 

ここでは、doWork が T を受け入れる別の関数を返すようにしました。それが何に解決されたかを見ることができ、実際にそれを呼び出すことができ、すべての型の制約に一致するものを渡すと正しく機能します。

追加した:

また、クラス階層はその動作を示すためにまったく必要ないことに注意してください。それらはまったく無関係である可能性があります。

trait A
trait B

def m[T <: A : Manifest](body: T => Unit) = manifest[T].toString()

m((x: B) => Unit)
//res0: String = A with B
于 2015-11-18T11:44:58.033 に答える
1

奇妙に見えますが、音を感じます。呼び出すこともできます。

s.doWork { x: Any => () }

T型パラメータはどういうわけか「無人」だと思います。Tメソッドはその上限以外について何も知ることができないBaseため、 のマニフェストを取得しますBase。しかし、それでは型の値を構築できないため、多くのことを行うことはできませんT…したがって、すべてが健全なままです。

署名をに変更してみてください

def doWork[T <: Base : Manifest](x: T)(body: T => Unit): String

次に、そのように使用することはできません。

s.doWork(123: Int) { x: Any => () }  // no
s.doWork(123: Any) { x: Any => () }  // no
于 2015-11-12T21:05:38.540 に答える