2

I m using Google Guava from a scala code. And an issue occurs when I m trying to use Int as a key type like in the example:

CacheBuilder.newBuilder()
    .maximumSize(2)
    .expireAfterWrite(24, TimeUnit.HOURS)
    .build(
      new CacheLoader[Int, String] {
        def load(path: Int): String = {
          path + "hello"
        }
      }
    )

It seems to be fine, but the inferred type of created object is LoadingCache[Int with AnyRef, String]:

  val cache: LoadingCache[Int with AnyRef, String] = CacheBuilder.newBuilder()
        .maximumSize(2)
        .expireAfterWrite(24, TimeUnit.HOURS)
        .build(
          new CacheLoader[Int, String] {
            def load(path: Int): String = {
              path + "hello"
            }
          }
        )

And the error occurs when I m trying to get an element like in this example:

cache.get(1)

Scala compiler error:

[ERROR] error: type mismatch;
[INFO]  found   : Int(1)
[INFO]  required: Int
[INFO]   cache.get(1)
[INFO]             ^

Can someone point me out why such error appears and what I m doing wrong?

ENV:

  • Google Guava 15.0
  • Scala 2.11.5
Sammers
  • 1,038
  • 2
  • 8
  • 20
  • Ok, this obviously cannot work with `Int with AnyRef`. So... What happens when you replace the declaration with `val cache: LoadingCache[Int, String] = `? Wouldn't it make more sense to post the compiler error for this case? Have you deliberately written down "LoadingCache[Int with AnyRef, String]" for some reason, or is it just some kind of artifact inserted by an imperfect IDE? – Andrey Tyukin Feb 20 '18 at 20:04
  • If i would change it to LoadingCache[Int, String] it won't compile as well. – Sammers Feb 20 '18 at 20:26
  • Yes, "it won't compile as well", but it would be closer to the root cause of the problem. I've explained both problems below, you are probably more interested in the solution in the second part. – Andrey Tyukin Feb 20 '18 at 21:55
  • Similar problems still occur with later versions of Scala and Guava: [Scala Guava type mismatch issue](https://stackoverflow.com/questions/49758706/scala-guava-type-mismatch-issue/49759011) – Andrey Tyukin Apr 10 '18 at 17:00

1 Answers1

4

On 1 not being an Int with AnyRef

The compile error in your question doesn't have anything to do with Guava. This snippet here produces the same error:

val h = new scala.collection.mutable.HashMap[Int with AnyRef, String]
h(3) = "hello"
println("Get 3: " + h.get(3))

gives

error: type mismatch;
found   : Int(3)
required: Int

This is caused by the Int with AnyRef: since Int is subtype of AnyVal, the intersection Int with AnyRef is empty, there simply cannot exist any instances with that type.


Now to the root cause of the problem.

The problem is that when you call .build(), the scala compiler cannot find a version that would work as .build[Int, String], because there is no version for unboxed integers. So instead, the compiler infers .build[AnyRef with Int, String], and builds an unusable cache structure.

To avoid this, use java.lang.Integer instead of Int. This here compiles and runs with guava 15.0 scala 2.11:

import com.google.common.cache._
import java.util.concurrent._

val cache: LoadingCache[java.lang.Integer, String] = CacheBuilder.newBuilder()
  .maximumSize(2)
  .expireAfterWrite(24, TimeUnit.HOURS)
  .build[java.lang.Integer, String](
    new CacheLoader[java.lang.Integer, String] {
      def load(path: java.lang.Integer): String = {
        path + "hello"
      }
    }
  )

cache.put(42, "hello, world")
println(cache.get(42))

It should work seamlessly with scala's Int, because scala autoboxes Ints into java.lang.Integer anyway.


Answers for similar errors:

  1. Scala Guava type mismatch issue
Community
  • 1
  • 1
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • See https://stackoverflow.com/questions/1269170/what-is-are-differences-between-int-and-integer-in-scala for more on boxing and the differences between Scala and Java – Rich Feb 21 '18 at 13:19
  • It works fine with Integer, but it is tricky to convert scala Int to java.lang.Integer – Sammers Feb 21 '18 at 13:56
  • @Sammers In what sense is it "tricky"? I can't think of any example where it would become tricky, even if I tried to make it tricky deliberately and on purpose. In Scala, `Int` and `java.lang.Integer` are converted back and forth completely transparently. Can you give a concrete example where it becomes "tricky"? – Andrey Tyukin Feb 21 '18 at 14:05