简体   繁体   中英

confusion in understanding scala's bound

I am reading Demystifying Scala Type System , in the 17th slide there is a snippet:

class Test[+A] {
  def test[B >: A](b: B): String = b.toString
}

The slide says method test will accept type A or any super type of A. But seems I could pass any type to test.

vat t = new Test[Int]
t.test("foo")
t.test(List(1, 2, 3))

I have the same confusion when I read Programming in Scala .

The important thing to remember here is that Any is a super type of any type, ie

Any >: A

In particular, assume

val t = new Test[Int]

This is, A is Int . Now we call

t.test("foo")

"foo" is of type String , but string is subtype of Any , and hence can be considered as such, therefore test[B >: A](b : B) can be called with b being "foo" and B being Any .

The following example should illustrate this, consider

class Test[+A](a : A) {
  def test[B >: A](b : B) : (A,B) = (a,b)
}

now, using

val t = new Test(3)
val x = t.test("foo")

we get

x: (Int, Any) = (3,foo)

Finally, to add some details, Scala will not always pick Any , but the least common supertype of A and B . This happens to be Any for Int and String (see http://www.scala-lang.org/old/node/128 ), but may be something different for other examples, eg, for

val s = new Test(Nil)
val y = s.test("foo")
val z = s.test(List(1))

we will get

y: (scala.collection.immutable.Nil.type, java.io.Serializable) = (List(),foo)
z: (scala.collection.immutable.Nil.type, List[Int]) = (List(),List(1))

Also note that the lower bound does not prevent passing subtypes of A

scala> val a = new Test(new AnyRef())
a: Test[java.lang.Object] = Test@6771a12

scala> a.test("foo")
res6: (java.lang.Object, java.lang.Object) = (java.lang.Object@78b99f12,foo)

So, the question is, what are lower type bounds useful for? One possible answer is that they can be used for "controlling" the types of "output" positions, as typically used for covariant type parameters, see, eg, http://docs.scala-lang.org/tutorials/tour/lower-type-bounds.html Roughly speaking, when appending an element to a (covariant) list of type A , you want to make sure that the resulting list is "at least" of type A . (I'm sorry for this being in parts hand-wavey, but as it goes beyond the scope of the original question, I just wanted to give a brief idea of why they are needed, for a full answer, it's probably better to create a new question)

The parameter can accept any type because of Any. However, I dont think this example is very clear. If you put this snippet into worksheet

if defined the return type is something related to B, you will see the problems

class Test[+A] {
  def test[B >: A](b: B): B = b
}


class A

class B extends A

val test = new Test[B]

val t = test.test("test")

The return type is not String , is Object . so basically, it loses the type reference.

The reason we need to define like this is A is covariant . In this case, it can only be used in return type not parameter because of the definition of Function1[-A, +B] . If use def test(b: A)..... it will have compilation errors:

covariant type A occurs in contravariant position in type A of value b def test(b: A): String = b.toString

Further, if you want to constrain the super type, you can have several options. one of it is using implicit to restrain the type.

class C

class CCC extends C

def test[B >: A](b: B)(implicit ev: B =:= C): B = b

val test = new Test[CCC]

test.test(new C)//ok

test.test("123")//compilation error

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