sharath chandra
sharath chandra

Reputation: 149

scala lower and upper bounds

class Queue[+T](
    private val leading: List[T],
    private val trailing: List[T]
) {
    def append[U >: T](x: U) =
      new Queue[U](leading, x :: trailing) // ...
}

class Fruit

class oranges extends Fruit

class apple extends Fruit

class diffAppale

val q1: Queue[Fruit] = new Queue[apple](List(new apple), List())
//> q1  : Test.Queue[Test.Fruit] = Test$Queue@30c7da1e

q1.append(new Fruit)                            
//> res0: Test.Queue[Test.Fruit] = Test$Queue@506e6d5e

q1.append(new oranges)                          
//> res1: Test.Queue[Test.Fruit] = Test$Queue@96532d6

q1.append(new diffAppale)  // i want to restrict this             
//> res2: Test.Queue[Object] = Test$Queue@3796751b

Here i could add to append function what ever object i can , i can see the resulting type is is demoted to a lowest common denominator

But i would like to have same behaviour as java, say def append[? super T](x: U) // here append function will take all objects which are supertype of T ,How can i achieve similar one in scala (implement super and extend for generics like java)

Upvotes: 2

Views: 854

Answers (1)

0__
0__

Reputation: 67280

I don't understand why you want to restrict this or why returning a more generic new immutable object poses a problem for you.

But if you don't want that variance, simply remove all the variance annotations:

class Queue[A](leading: List[A], trailing: List[A]) {
  def append(x: A) = new Queue[A](leading, x :: trailing) // ...
}

class Fruit
class Orange extends Fruit
class Apple  extends Fruit
class NotFruit

val q1: Queue[Fruit] = new Queue(List(new Apple), Nil)
val q2 = q1.append(new Fruit)  // ok
val q3 = q2.append(new Orange) // ok

q1.append(new NotFruit) // error - found NotFruit, required Fruit

As to the question:

append function will take all objects which are supertype of T

This is what your original code already does. Note that Any is super-type of all types. Since the argument is in covariant position, it is always possible to pass a value with a sub-type of Any for an argument of expected type Any. That's life :) Scala just feels different than Java because it is built around declaration site variance and not use site variance (arguably a better decision than Java's generics).

If you want further constraints, you can ask for an evidence parameter such as

def append[B >: A](x: B)(implicit ev: B <:< Seedless): Queue[B]

Or put an upper bound (this cannot be the same as A because of A's variance):

def append[B >: A <: Fruit](x: B): Queue[B]

But: it really doesn't make sense IMHO.

Upvotes: 1

Related Questions