ShatyUT
ShatyUT

Reputation: 1365

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.

Upvotes: 3

Views: 4926

Answers (3)

Eun Woo Song
Eun Woo Song

Reputation: 740

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

Upvotes: 0

Jens Schauder
Jens Schauder

Reputation: 81882

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

Upvotes: 4

dhg
dhg

Reputation: 52681

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 Foos and not FooImplBs! So we have a problem.

Upvotes: 6

Related Questions