Scala: вызов методов с / без () с переопределенными последствиями - PullRequest
0 голосов
/ 27 августа 2018

Вот определение метода, который неявно использует ExecutionContext и позволяет клиенту переопределить его. Для проверки используются два контекста выполнения:

val defaultEc = ExecutionContext.fromExecutor(
    Executors.newFixedThreadPool(5))

Имена потоков выглядят как: 'pool-1-thread-1' to 'pool-1-thread-5'

И второй от Scala:

scala.concurrent.ExecutionContext.Implicits.global

Имена потоков выглядят так: 'scala-execute-context-global-11'

Клиент может переопределить неявное значение по умолчанию через:

implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global

К сожалению, он может быть переопределен только тогда, когда метод с неявным вызовом вызывается без ():

val r = FutureClient.f("testDefault") //prints scala-execution-context-global-11

не работает:

val r = FutureClient.f("testDefault")() //still prints: pool-1-thread-1

Вопрос в том, ПОЧЕМУ это работает так? Потому что это делает его намного сложнее для клиентов API

Вот полный код для запуска и воспроизведения:

object FutureClient {
  //thread names will be from 'pool-1-thread-1' to 'pool-1-thread-5'
  val defaultEc = ExecutionContext.fromExecutor(
    Executors.newFixedThreadPool(5))

  def f(beans: String)
           (implicit executor:ExecutionContext = defaultEc)
  : Future[String] = Future {
    println("thread: " + Thread.currentThread().getName)
    TimeUnit.SECONDS.sleep(Random.nextInt(3))
    s"$beans"
  }
}

class FutureTest {
  //prints thread: pool-1-thread-1
  @Test def testFDefault(): Unit ={
    val r = FutureClient.f("testDefault")
    while (!r.isCompleted) {
      TimeUnit.SECONDS.sleep(2)
    }
  }

  //thread: scala-execution-context-global-11
  @Test def testFOverridable(): Unit ={
    implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global
    val r = FutureClient.f("testDefault")
    while (!r.isCompleted) {
      TimeUnit.SECONDS.sleep(2)
    }
  }

  //prints pool-1-thread-1, but not 'scala-execution-context-global-11'
  //cause the client invokes f with () at the end
  @Test def testFOverridableWrong(): Unit ={
    implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global
    val r = FutureClient.f("testDefault")()
    while (!r.isCompleted) {
      TimeUnit.SECONDS.sleep(2)
    }
  }
}

Я уже обсуждал несколько связанных тем, но они связаны с определением API, поэтому это новая проблема, не охватываемая этими темами.

1 Ответ

0 голосов
/ 27 августа 2018

Шаблоны Scala, которых следует избегать: неявные аргументы со значениями по умолчанию

f("testDefault") (или f("testDefault")(implicitly)) означает, что неявный аргумент берется из неявного контекста.

f("testDefault")(newEc) означает, что вы явно указываете неявный аргумент. Если вы пишете f("testDefault")(), это означает, что вы указываете неявный аргумент в явном виде, но поскольку значение не указано, оно должно быть взято из значения по умолчанию.

...