Reputation: 41939
Given:
scala> abstract class Foo[A] {
| def f: A
| }
defined class Foo
, and an implementation
scala> class FooImpl extends Foo[Any] {
| def f: Any = "foo"
| }
defined class FooImpl
and then an instantiation
scala> new FooImpl().f
res0: Any = foo
res0
has type Any
, as I'd expect since FooImpl
uses Any
as its type parameter to Foo
.
And, given a second implementation:
scala> class FooImpl2 extends Foo[Any] {
| override def f= "foo"
| }
defined class FooImpl2
and an instantiation and call to f
:
scala> new FooImpl2().f
res1: String = foo
Why is res1
's type a String
?
Whereas, given:
scala> def g[A](x: A):A = x
g: [A](x: A)A
I can pass y
, an Int
:
scala> val y: Int = 55
y: Int = 55
to g
scala> g[AnyVal](y)
res3: AnyVal = 55
and get back an AnyVal
.
Finally,
scala> g(55)
res5: Int = 55
returns an Int
, as expected.
However, I would've expected FooImpl2#f
to have returned an Any
given FooImpl2
's extends Foo[Any]
.
Why not?
Upvotes: 0
Views: 875
Reputation: 15086
A subclass can narrow the return types of the methods that it overrides.
The return type of f
in Foo[Any]
is Any
. FooImpl2
is a subtype of Foo[Any]
and you didn't specify f
s return type so the compiler infers that it is String
, which is a subtype of Any
and thus satisfies all constraints.
Upvotes: 1
Reputation: 55569
When you override a member of an abstract class or trait, you are allowed to narrow its type to something more specific. It doesn't appear that way here, because you're relying on type inference for override def f = "foo"
, but it's really override def f: String = "foo"
.
This is legal:
abstract class Foo[A] {
def f: A
}
class FooImpl2 extends Foo[Any] {
override def f: String = "foo"
}
String
still conforms to the type parameter A = Any
, but f
has been refined in FooImpl2
to String
.
Here is an example without the type parameter:
abstract class A { def f: Any }
class B extends A { override def f: String = "a" }
g[AnyVal](y)
is a just very different example. Since you are manually supplying the type parameter of AnyVal
to g
, you are asking the compiler to make sure y
is AnyVal
, but it doesn't matter if it is some more specific type (a more specific type returned by method will always be up-cast to the return type). In FooImpl2
you are simply changing the signature of f
.
Upvotes: 7
Reputation: 8673
When you override
a method, you can make it return more specific type. In this line
override def f= "foo"
you didn't specify return type and it was inferred to be String
Upvotes: 4