如何使用Java中的scalaz.Reader

问题描述 投票:1回答:1

我有一个用Scala编写的服务,它使用scalaz.Reader进行DI并对其进行测试。

在测试中,定义了op函数,以组成服务的功能。

import scala.util.{Failure, Success, Try}
import scalaz.{Reader => ScalazReader}

trait AccountServiceScalazReader[Account, Amount, Balance] {
  def open(no: String, name: String, openingDate: Option[Date]): ScalazReader[AccountRepository, Try[Account]]
  def close(no: String, closeDate: Option[Date]): ScalazReader[AccountRepository, Try[Account]]
  def debit(no: String, amount: Amount): ScalazReader[AccountRepository, Try[Account]]
  def credit(no: String, amount: Amount): ScalazReader[AccountRepository, Try[Account]]
  def balance(no: String): ScalazReader[AccountRepository, Try[Balance]]
}

object AccountServiceScalazReader extends AccountServiceScalazReader[Account, Amount, Balance] {

  def open(no: String, name: String, openingDate: Option[Date]) = ScalazReader { (repo: AccountRepository) =>
    repo.query(no) match {
      case Success(Some(a)) => Failure(new Exception(s"Already existing account with no $no"))
      case Success(None) =>
        if (no.isEmpty || name.isEmpty) Failure(new Exception(s"Account no or name cannot be blank") )
        else if (openingDate.getOrElse(today) before today) Failure(new Exception(s"Cannot open account in the past"))
        else repo.store(Account(no, name, openingDate.getOrElse(today)))
      case Failure(ex) => Failure(new Exception(s"Failed to open account $no: $name", ex))
    }
  }

  def close(no: String, closeDate: Option[Date]) = ScalazReader { (repo: AccountRepository) =>
    repo.query(no) match {
      case Success(Some(a)) =>
        if (closeDate.getOrElse(today) before a.dateOfOpening)
          Failure(new Exception(s"Close date $closeDate cannot be before opening date ${a.dateOfOpening}"))
        else repo.store(a.copy(dateOfClosing = closeDate))
      case Success(None) => Failure(new Exception(s"Account not found with $no"))
      case Failure(ex) => Failure(new Exception(s"Fail in closing account $no", ex))
    }
  }

  def debit(no: String, amount: Amount) = ScalazReader { (repo: AccountRepository) =>
    repo.query(no) match {
      case Success(Some(a)) =>
        if (a.balance.amount < amount) Failure(new Exception("Insufficient balance"))
        else repo.store(a.copy(balance = Balance(a.balance.amount - amount)))
      case Success(None) => Failure(new Exception(s"Account not found with $no"))
      case Failure(ex) => Failure(new Exception(s"Fail in debit from $no amount $amount", ex))
    }
  }

  def credit(no: String, amount: Amount) = ScalazReader { (repo: AccountRepository) =>
    repo.query(no) match {
      case Success(Some(a)) => repo.store(a.copy(balance = Balance(a.balance.amount + amount)))
      case Success(None) => Failure(new Exception(s"Account not found with $no"))
      case Failure(ex) => Failure(new Exception(s"Fail in credit to $no amount $amount", ex))
    }
  }

  def balance(no: String) = ScalazReader((repo: AccountRepository) => repo.balance(no))
}

测试:

import org.junit.{Assert, Test}

import scala.util.Try

class AccountServiceScalazReaderTest {

  import com.savdev.fp.monad.di.reader.AccountServiceScalazReader._
  def op(no: String):scalaz.Reader[AccountRepository, Try[Balance]]
    = for {
    _ <- credit(no, BigDecimal(100))
    _ <- credit(no, BigDecimal(300))
    _ <- debit(no, BigDecimal(160))
    b <- balance(no)
  } yield b

  @Test def testOpComposition: Unit = {
    val newOp = for {
      _ <- open("a-123", "Alex", Option.empty)
      b <- op("a-123")
    } yield b

    val balance = newOp run (new TestAccountRepository)
    Assert.assertTrue(balance.isSuccess)
    Assert.assertEquals(Balance(240), balance.get)
    println(balance)
  }

  @Test def testOpCompositionNotExistingAccount: Unit = {
    val balance = op("a-123") run (new TestAccountRepository)
    Assert.assertTrue(balance.isFailure)
    Assert.assertEquals("No account exists with no a-123", balance.failed.get.getMessage)
    println(balance)
  }
}

现在我试图从Java代码编写相同的测试。我甚至无法定义op函数的签名:

import scala.util.Try;
import scalaz.Reader;

public class AccountServiceScalazReaderFromJavaTest {

  scalaz.Reader<AccountRepository, Try<Balance>> op(String no) {
    return null;
  }
}

我现在要:

AccountServiceScalazReaderFromJavaTest.java:[12,9] cannot find symbol
  symbol:   class Reader
  location: package scalaz

我错过了什么?如何从Java代码实现op

java scala scalaz scala-java-interop reader-monad
1个回答
1
投票

Scala具有类型别名,这在Java中是不存在的。 Reader不是一个类,它是一个带有伴随对象的别名:

type Reader[E, A] = ReaderT[Id, E, A]

object Reader extends scala.Serializable {
  def apply[E, A](f: E => A): Reader[E, A] = Kleisli[Id, E, A](f)
}

type ReaderT[F[_], E, A] = Kleisli[F, E, A]

val ReaderT = Kleisli

所以我想这会更接近

Kleisli<?, AccountRepository, Try<Balance>> op(String no) {
  return null;
}

虽然Java没有HKT,所以我想在javac接受Id[_]之前可能会有一些试验和错误(特别是因为Id[_]也是类型别名,而不是类)。

© www.soinside.com 2019 - 2024. All rights reserved.