开发者

Method return type covariance

开发者 https://www.devze.com 2023-02-09 04:27 出处:网络
How can I define that method returns List[+AnyRef]? I tried: def a[T <: AnyRef](): List[T] = List[AnyRef]()

How can I define that method returns List[+AnyRef]? I tried:

def a[T <: AnyRef](): List[T] = List[AnyRef]() 
开发者_运维技巧

But for some reason it does not compile.

EDIT: according to Wong I should use

def a[T <: AnyRef](): List[T] = List[T]()

but is there any way to be able to return any subtype of AnyRef, for example

def a[T <: AnyRef](): List[T] = if (value) List[T]() else List[Option[String]]()

Here Option[String] is descendant of Anyref, but compiler does not accept it

So main question is if I can declare method with covariant return type like List[+AnyRef]


Let's make a couple observations and experiment with a few ways of letting the compiler to decide on your return type:

1) Notice the statement if (value) List[T]() else List[Option[String]]() returns 2 different specific types but the if statement must return the same type from its then and else clauses. So when this statement returns a value, the compiler will need to infer the most general type for the 2 clauses to bring in a consistent constraint.

2) Notice that type variable T is dependent on the exact type you pass in when you call a(), for example a[scala.io.Source](). In the method declaration you gave T an upper bound T <: AnyRef, which means the compiler has to find the most general type that is the union of any type that is a subtype of AnyRef and Option[String].

3) Notice the return type that is inferred by the compiler by removing the return type declaration. i.e. def a[T <: AnyRef]() = if (true) List[T]() else List[Option[T]](). The compiler gave a() a return type List[AnyRef]. This sort of make sense because that is the only possibility for the most general type between anything T that is a subtype of AnyRef and Option[of that anything T].

4) Now try def a[T <: AnyRef]() = if (true) List[T]() else List[Option[String]](). The return type inferred is now List[java.lang.Object]. The reason is the String class in Scala 2.8 is actually java.lang.String, so according to my best guess, now the most general type has to escape the scala.* hierarchy and end up in java.lang.Object for unknown reasons.

5) Since AnyRef is really just alias of java.lang.Object, you can do def a[T <: AnyRef](): List[AnyRef] = if (true) List[T]() else List[Option[String]]() to force a return type of List[AnyRef].

If you just want to return any subtype of AnyRef, you basically have to do this:

def a(): List[AnyRef] = ...

which basically returns the super class, and you have to cast the returned List[AnyRef] down using .asInstanceOf[T]. Alternatively:

def a[T <: AnyRef](): List[T] = List[T]()

will gives you a specific type T, but you can't return 2 different types in an if statement like in your example, where one may be more specific and the other, and expect it to always return the more specific type supplied by you when you call the method. Because the compiler has no way to guarantee the type in your if statement will always be List[T] just by doing type checking. Did I make it clearer?


Your definition

def a[T <: AnyRef](): List[T] = List[AnyRef]() 

doesn't compile because the return value is a List[AnyRef], which isn't a List[T]. The other way around does:

def a[T <: AnyRef](): List[AnyRef] = List[T]()

and corresponds to your question literally, but Wong's answer is likely to be more useful.


Think about code calling your method. For instance,

val x = a()

What is the type of x? One thing you cannot say is that the type of x depends on something -- it can only depend on the static context of the line above and the method's type signature. So the example in which you return T or Option[String] can never work, because there is no way to tell which will be returned from the method signature.

What, exactly, is your use case? How do you intend to use it, that you want such a thing?

0

精彩评论

暂无评论...
验证码 换一张
取 消