我想將工廠方法與泛型一起使用,它可以與特定的實作一起使用。在服務類中,我想要型別安全,但在控制器中操作只知道介面。
代碼
我定義了不同型別的操作型別
trait Transaction {
val amount: BigDecimal
}
case class CreditCardTransaction(amount: BigDecimal, ccNumber: String, expiry: String) extends Transaction
case class BankTransaction(amount: BigDecimal, bankAccount: String) extends Transaction
和可以與特定操作型別一起使用的服務
trait Service[T <: Transaction] {
def transfer(transaction: T)
}
class CCService() extends Service[CreditCardTransaction] {
override def transfer(transaction: CreditCardTransaction): Unit = println("pay with cc")
}
class TTService() extends Service[BankTransaction] {
override def transfer(transaction: BankTransaction): Unit = println("pay with telex transfer")
}
我用具體實體創建了工廠
class PaymentSystemFactory(ccService: CCService, ttService: TTService) {
def getService(paymentMethod: String) = paymentMethod match {
case "cc" => ccService
case "tt" => ttService
}
}
和決議器從外部服務獲取特定事務
object Parser {
def parse(service: Service[_ <: Transaction]) = service match {
case _: Service[CreditCardTransaction] => CreditCardTransaction(100, "Name", "01/01")
case _: Service[BankTransaction] => BankTransaction(100, "1234")
}
}
但是該代碼不想從 PaymentSystemFactory 方法中編譯由于提供的型別不匹配:
object App {
val factory = new PaymentSystemFactory(new CCService, new TTService)
val service = factory.getService("cc") // return Service[_ >: CreditCardTransaction with BankTransaction <: Transaction]
val transaction: Transaction = Parser.parse(service)
service.transfer(transaction) // Failed here: Required _$1 found Transaction
}
如果可能,由于工廠方法呼叫,我很樂意避免型別擦除,并想知道為什么該代碼不起作用
uj5u.com熱心網友回復:
您在這里所做的(顯然是意外?)是創建一個廣義代數資料型別,或簡稱為 GADT。如果您想了解有關此功能的更多資訊,這可能是一個有用的搜索詞。
至于如何實作:方法的型別簽名parse需要反映回傳的事務型別與服務的事務型別匹配。
此外,您不能這樣做case _: Service[CreditCardTransaction],由于擦除而無法正常作業。使用case _: CCService來代替。
嘗試這個:
object Parser {
def parse[A <: Transaction](service: Service[A]): A = service match {
case _: CCService => CreditCardTransaction(100, "Name", "01/01")
case _: TTService => BankTransaction(100, "1234")
}
}
您還需要更改呼叫代碼:
object App {
val factory = new PaymentSystemFactory(new CCService, new TTService)
factory.getService("cc") match {
case service: Service[a] =>
val transaction: a = Parser.parse(service)
service.transfer(transaction)
}
}
請注意,match并不用于實際區分多種情況。相反,a在這種情況下,它在這里的唯一目的是為事務型別命名。這是 Scala 語言中最晦澀的特性之一。當您對通配符型別進行模式匹配并使用小寫名稱a作為型別引數時,它不會檢查該型別a(就像它檢查大寫名稱一樣),但它會創建一個新的型別變數以后可以使用。在這種情況下,它用于宣告transaction變數,也隱式地呼叫Parser.parse方法。
uj5u.com熱心網友回復:
在我制定以下解決方案時,@Mathias 提出了另一個我覺得非常好的解決方案。但我仍然將其發布為可能有趣的替代方案。
您可以使用型別成員,而不是型別引數(即泛型):
trait Service {
type T <: Transaction
def transfer(transaction: T): Unit
}
class CCService() extends Service {
type T = CreditCardTransaction
override def transfer(transaction: CreditCardTransaction): Unit = println("pay with cc")
}
class TTService() extends Service {
type T = BankTransaction
override def transfer(transaction: BankTransaction): Unit = println("pay with telex transfer")
}
// need to use asInstanceOf, I don't know how to tell scala that the type is safe
def parse(service: Service): service.T = service match {
case _: CCService => CreditCardTransaction(100, "Name", "01/01").asInstanceOf[service.T]
case _: TTService => BankTransaction(100, "1234").asInstanceOf[service.T]
}
val factory = new PaymentSystemFactory(new CCService, new TTService)
val service = factory.getService("tt")
val transaction = parse(service) // transaction has type service.T
service.transfer(transaction)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/311144.html
上一篇:在使用實作類的介面中實作泛型方法
