Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CollectionConverters API for Scala 2.11/2.12 #208

Closed
dwijnand opened this issue May 25, 2019 · 8 comments · Fixed by #217
Closed

CollectionConverters API for Scala 2.11/2.12 #208

dwijnand opened this issue May 25, 2019 · 8 comments · Fixed by #217
Assignees

Comments

@dwijnand
Copy link
Member

To migrate the code and avoid the depreciation messages in 2.13.

@dwijnand
Copy link
Member Author

Discussed on Monday next steps are:

@DaniRey
Copy link

DaniRey commented May 31, 2019

The following change scala/scala@05f8f18#diff-2efa831ba3194fedd2a132bb246f2e22 "Add jdk.CollectionConverters, deprecate collection.JavaConverters" is a challenge for cross-compiled projects.

Adding "jdk.CollectionConverters" to scala-collection-compat would give cross-compiled projects the opportunity to avoid deprecation warnings, by switching to "jdk.CollectionConverters".

@dwijnand
Copy link
Member Author

Another option I've see taken is disabling deprecation warnings when building with Scala 2.13. Or leaving them on but disabling fatal warnings when on 2.13.

@sjrd
Copy link
Member

sjrd commented May 31, 2019

@DaniRey You can cross-compile warning-free with this helper beast:

/** Magic to get cross-compiling access to `scala.jdk.CollectionConverters`
 *  with a fallback on `scala.collection.JavaConverters`, without deprecation
 *  warning in any Scala version.
 */
object JDKCollectionConvertersCompat {
  object Scope1 {
    object jdk {
      type CollectionConverters = Int
    }
  }
  import Scope1._

  object Scope2 {
    import scala.collection.{JavaConverters => CollectionConverters}
    object Inner {
      import scala._
      import jdk.CollectionConverters
      val Converters = CollectionConverters
    }
  }

  val Converters = Scope2.Inner.Converters
}

which you can then use as

import JDKCollectionConvertersCompat.Converters._

val list = List(1, 2, 3)
val jList = list.asJava
println(jList)

@dwijnand
Copy link
Member Author

:mind_blown: I actually would need you to explain to me how that works...

@sjrd
Copy link
Member

sjrd commented May 31, 2019

It works as follows.

When compiling on 2.13

The import jdk.CollectionConverters in object Inner of course resolves to scala.jdk.CollectionConverters, because of the import scala._ just above. The latter takes precedence over import Scope1._, because it is in a narrower scope.

Then of course JDKCollectionConvertersCompat.Converters is scala.jdk.CollectionConverters because the import jdk.CollectionConverters is a narrower scope than the import scala.collection.{JavaConverters => CollectionConverters}.

That means that the import scala.collection.{JavaConverters => CollectionConverters} is unused, and therefore does not emit a deprecation warning (yeah!).

The contents of Scope1 does not play a role in this scenario. It's dead code.

When compiling on 2.12 or earlier

The import jdk.CollectionConverters in object Inner does not find any jdk in scala._, so it looks further in the import Scope1._ and hence finds Scope1.jdk.CollectionConverters.

You'd then expect that val Converters = CollectionConverters also resolves to that thing, right? Except it doesn't, because it's looking for a term, but Scope1.jdk.CollectionConverters is a type. So it skips that import! It then finds the import scala.collection.{JavaConverters => CollectionConverters} and therefore resolves to the (not-yet-deprecated-in-2.12) scala.collection.JavaConverters.

The contents Scope1 ends up not being used. It's dead code. But it had to be there so that the import jdk.CollectionConverters line (which was needed in 2.13) does not yield a compile error.

@dwijnand
Copy link
Member Author

I follow!! Thanks for that. <3

@dwijnand dwijnand self-assigned this Jun 6, 2019
@som-snytt
Copy link

I wanted to find a solution that exploited the fix that a local import now has binding precedence over a definition in another file, such as the package object.

Given a package object, which could be separately compiled or only for 2.12:

$ cat package.scala

package object convertible {
  object jdk {
    val CollectionConverters = scala.collection.JavaConverters
  }
}

then this works in the obvious way:

$ cat code.scala 

package convertible {

  class C {
    import scala._
    import jdk.CollectionConverters._

    val old: java.util.List[Int] = new java.util.ArrayList[Int]()

    def f = old.asScala
  }

  object Main {
    def main(args: Array[String]): Unit = println {
      new C().f
    }
  }
}

where obvious means that jdk always binds to the import in 2.13 and the package object definition in 2.12. But this doesn't avoid the deprecation unless using a 2.12 source dir or a shim project where the deprecation is not bothersome. This doesn't buy much except that if the behaviors were different, at least you'd select one API in 2.13 and the old in 2.12. So maybe the trick would be useful in a different context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants