【问题标题】:scala - implementing trait method with parameter that is child of expected typescala - 使用预期类型的​​子参数实现 trait 方法
【发布时间】:2012-08-20 20:48:31
【问题描述】:

我对 Scala 很陌生,如果这是一个非常简单的问题,请原谅我,但我找不到任何可以帮助我的东西,或者我找不到正确的搜索词。我怎样才能做到这一点?

scala> trait Foo
defined trait Foo

scala> class FooImpl extends Foo
defined class FooImpl

scala> trait Bar { def someMethod(foo: Foo) }
defined trait Bar

scala> class BarImpl extends Bar { def someMethod(foo: FooImpl) {} }
<console>:10: error: class BarImpl needs to be abstract, since method someMethod in trait Bar of type (foo: Foo)Unit is not defined
(Note that Foo does not match FooImpl)
       class BarImpl extends Bar { def someMethod(foo: FooImpl) {} }

既然 Foo 是一个特征,为什么 FooImpl 不匹配 Foo?我猜我需要在 Bar 中更改 someMethod 的签名,以说明我期待扩展 Foo 或“with Foo”的东西,但我似乎找不到这方面的文档。

【问题讨论】:

    标签: scala methods parameters match extends


    【解决方案1】:

    问题在于Bar trait 的someMethod 声明指定任何类型的Foo 都可以作为参数传递。你可以把它想象成它的“契约”。合同规定Bar 的任何实现都将有一个方法someMethod 可以接受任何类型的Foo

    您的BarImpl 类是Bar 的实现,并且具有someMethod 实现。不幸的是,它对someMethod 的实现only 接受FooImpl 类型的Foo 对象:不是任何 类型的Foo。由于它不允许您传入不是FooImpl 对象的Foo 对象,因此它违反了特征定义指定的契约。实施不能比合同规定的更严格。

    举个例子:

    class FooImplB extends Foo
    val bar: Bar = new BarImpl
    val foo: Foo = new FooImplB
    bar.someMethod(foo)
    

    这里我们声明了一个名为barBar 和一个名为fooFoo。根据Foo 的定义,我应该可以将foo 传递给bar.someMethod。除了BarImpl.someMethod 只接受FooImplFoos 而不是FooImplBs!所以我们有一个问题。

    【讨论】:

    • 我想我已经习惯在 Groovy 中这样做了,我忘记了你不能在 Java 中这样做。
    【解决方案2】:

    dhg 解释了为什么这不起作用以及为什么您可能并不真正想要它。

    但如果你仍然想要它,你可以这样做:

    trait Foo
    
    class FooImpl extends Foo
    
    trait Bar[F <: Foo] { def someMethod(foo: F) }
    
    class BarImpl extends Bar[FooImpl] {
        def someMethod(foo: FooImpl) {}
    }
    

    【讨论】:

    • 接受了这个,因为它解决了我的问题。 “F <: foo>
    • Yes F <: foo>
    【解决方案3】:

    Jens Schauder 的回答有效,但强制您在特征签名中定义类型。相反,您可以在方法级别执行相同操作:

    scala> trait Foo
    defined trait Foo
    
    scala> class FooImple extends Foo
    defined class FooImple
    
    scala> trait Bar { def methodA[T <: Foo](foo: T) }
    defined trait Bar
    
    scala> class BarImpl extends Bar { def methodA[FooImpl](foo: FooImpl){} }
    defined class BarImpl
    

    【讨论】:

    • 是的,这是 IMO 更好的答案。
    • 这个答案是错误的。即使您修正了错字 (FooImple / FooImpl),最后一行中的 FooImpl 是一个新的类型参数,它隐藏了 FooImpl 类,因此您可以使用字符串或整数作为参数调用它,例如,这不是你想要的。 Jens 的答案是正确的。
    • @TravisBrown 你是对的。我没有注意到真正指出问题的错字。即使 FooImpl 尚未定义,编译器也不会抱怨。我验证了 Jens 在 Eclipse 中为我的项目工作的答案。我会换回答案。
    猜你喜欢
    • 1970-01-01
    • 2014-10-09
    • 1970-01-01
    • 1970-01-01
    • 2017-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-30
    相关资源
    最近更新 更多