Swapping elements of two Arrays in Scala with wild

2019-09-02 08:38发布

问题:

I want to do something like the following:

class A[T] (_arr: Array[T]) { var arr = _arr }
class C {
  def foo[T <: A[_]](a: Array[T]){
    var ele1 = a(0)
    var ele2 = a(1)
    for(i <- 0 until ele1.arr.length) {
      val temp = ele1.arr(i)
      ele1.arr(i) = ele2.arr(i)
      ele2.arr(i) = temp
    }
  }
}

But I get the following compiler error:

<console>:15: error: type mismatch;
 found   : temp.type (with underlying type Any)
 required: _$1
             ele2.arr(i) = temp
                           ^

I'm not sure why ele1.arr(i) = ele2.arr(i) would work and ele2.arr(i) = temp would not.

EDIT: According to Rex's Suggestion, I changed it to the following:

class A[T] (_arr: Array[T]) { var arr = _arr }
class C {
  def foo[T, B <: A[T]](b: Array[B]){
    var ele1 = b(0)
    var ele2 = b(1)
    for(i <- 0 until ele1.arr.length) {
      val temp = ele1.arr(i)
      ele1.arr(i) = ele2.arr(i)
      ele2.arr(i) = temp
    }
  }
}

and now that compiles fine. But when I do a test case, as follows:

object Test extends App {
  var p = Array.ofDim[A[Int]](2)
  var g1 = new A[Int](Array(1, 2, 3, 4, 5))
  var g2 = new A[Int](Array(6, 7, 8, 9, 10))
  p(0) = g1
  p(1) = g2

  (new C).foo(p)
  p.foreach{ x => println(x.arr.deep.mkString(",")) }
}

I get the following compiler error:

src/Test.scala:22: error: inferred type arguments [A[Int],Nothing] do not conform to method foo's type parameter bounds [B <: A[T],T]
  (new C).foo(p)
          ^
src/Test.scala:22: error: type mismatch;
 found   : Array[A[Int]]
 required: Array[B]
  (new C).foo(p)
              ^
two errors found

I understand that I can do (new C).fooInt, A[Int], but I'd really like to have the compiler infer it. Otherwise it kind of defeats the purpose of a lot of what I'm doing.

回答1:

I don't understand why you're using wildcards here instead of parameterizing the element type. You're blinding the compiler and making it harder for it to make sure you are doing something sensible. In particular, it realizes that since arrays are invariantly typed, ele1.arr(i) and ele2.arr(i) are the same type and thus can be assigned. But when you assign to temp, it realizes that it has no idea what that type might actually be, so it infers Any, and then of course that won't fit back into the array because it's not actually Any; it's just lost track of what it should be.

class C {
  def foo[T, B <: A[T]](b: Array[B]){
    var ele1 = b(0)
    var ele2 = b(1)
    for(i <- 0 until ele1.arr.length) {
      val temp = ele1.arr(i)
      ele1.arr(i) = ele2.arr(i)
      ele2.arr(i) = temp
    }
  }
}

allows the compiler to know the types all the way through, which keeps it happy, and you safer.