简体   繁体   中英

scala - implementing trait method with parameter that is child of expected type

I'm very new to Scala so forgive me if this is a real easy question but I could not find anything to help me or I could not figure out the right search terms. How can I make this work?

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) {} }

Why doesn't FooImpl match Foo since Foo is a trait? I'm guessing I need to alter the signature of someMethod in Bar to say that I'm expecting something that extends Foo or "with Foo" but I can't seem to find documentation for this.

The problem is that the Bar trait's someMethod declaration specifies that any kind of Foo can be passed as an argument. You can think of this as its "contract". The contract says that any implementation of Bar will have a method someMethod that will accept any kind of Foo .

Your BarImpl class is an implementation of Bar and has a someMethod implementation. Unfortunately, its implementation of someMethod only accepts FooImpl kinds of Foo objects: not any kind of Foo . Since it doesn't allow you to pass in Foo objects that aren't FooImpl objects, it violates the contract specified by the trait definition. Implementations can't be more restrictive than the contract specifies.

As an example:

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

Here we declare a Bar called bar and a Foo called foo . According to the definition of Foo I should be able to pass foo into bar.someMethod . Except that BarImpl.someMethod only accepts FooImpl kinds of Foo s and not FooImplB s! So we have a problem.

dhg explained why this doesn't work and why you probably don't really want it.

But if you still want it, you can do it like this:

trait Foo

class FooImpl extends Foo

trait Bar[F <: Foo] { def someMethod(foo: F) }

class BarImpl extends Bar[FooImpl] {
    def someMethod(foo: FooImpl) {}
}

Jens Schauder's answer works but forces you to define the type in the trait signature. Instead, you can do the same on the method level:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM