What is Scala trying to tell me and how do I fix t

2020-06-17 14:09发布

问题:

I am in the process of learning Scala and today I felt confident to introduce it to one of our projects.

The application does a lot of JPA / Hibernate stuff and I started implementing one of the Java interfaces in Scala. All went well, until I tried to translate some unittest-code to Scala.

I make a lot of use of Easymock, the code is quite self explanatory. I guess the problem is, that Scala does not allow me to return a typed java.util.List where it expects an untyped one. Unfortunately I have no idea how to make Scala cast something that expects type parameters to something without.

Code that illustrates/reproduces my problem:

package some.package.name

import java.util.ArrayList
import java.util.List
import javax.persistence.Query
import org.easymock.EasyMock._
import org.junit.Assert._
import org.junit.Test

class MyGreatScalaTest {

  @Test
  def testSomething() : Unit = {

    val mockQuery: Query = createMock(classOf[Query])
    val mockResult: List[String] = new ArrayList[String]
    mockResult.add("great value")

    expect(mockQuery.getResultList).andReturn(mockResult)
    replay(mockQuery)

    assertEquals(
      (mockQuery.getResultList.asInstanceOf[List[String]]).get(0),
      "great value")

    verify(mockQuery)
  }
}

Which produces the following compiler error:

[WARNING]  found   : java.util.List[String]
[WARNING]  required: java.util.List[?0] where type ?0
[WARNING]     expect(mockQuery.getResultList).andReturn(mockResult)
[WARNING]                                               ^     

As you can see I need the mock to implement the interface of javax.persistence.Query, which returns a java.util.List.

回答1:

javax.persistence.Query#getResultList returns a raw type List, as opposed to a *cooked8 type like List[String]. Java generified much of the standard library in version 1.5 but had to be backwards compatible with binaries and sources written for 1.4.

The Scala compiler tolerates such nasty types, but translates it to List[_], which is shorthand for List[?0] forSome { type ?0 }. This is known as an existential type, and it means that the element type of the List is some specific type, even though we don't know exactly which one!

Easymock requires that the argument to andReturn is of the same type as the type of the argument passed to expect, our troublesome existential type.

I expect that this will fix the problem:

expect(mockQuery.getResultList.asInstanceOf[List[String]]).andReturn(mockResult)