简体   繁体   English

具有泛型类型的 Scala 类

[英]Scala class with generic types

I am trying to write a thin Scala wrapper around the Java Aws Lambda Client.我正在尝试围绕 Java Aws Lambda 客户端编写一个瘦 Scala 包装器。

This class should accept 2 generic parameters:这个类应该接受 2 个通用参数:

  • An input type A - which should be either a case class, which gets serialized to Json and sent to the Lambda function, OR Nothing/Unit type, in case the Lambda function intended to be called doesn't take any input parameters.输入类型 A - 应该是 case 类,它被序列化为 Json 并发送到 Lambda 函数,或者 Nothing/Unit 类型,以防要调用的 Lambda 函数不接受任何输入参数。
  • An input type B - which should be a case class which is the actual deserialized Json->case class that the Lambda function returned, OR Unit in case the Lambda function doesn't return anything.输入类型 B - 它应该是一个 case 类,它是 Lambda 函数返回的实际反序列化的 Json->case 类,或者如果 Lambda 函数不返回任何内容,则为单位。

Something along the lines of:类似的东西:

import com.amazonaws.services.lambda.model.InvokeRequest
import com.amazonaws.services.lambda.{AWSLambda, AWSLambdaClientBuilder}
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read, write}

class LambdaInvoker[A <: AnyRef, B <: AnyRef](val client: AWSLambda = AWSLambdaClientBuilder.defaultClient()) {

  implicit val serialization: Serialization.type = org.json4s.native.Serialization

  def call(input: A, function: String): B = {

    val request = new InvokeRequest().withFunctionName(function).withPayload(write(input))
    val result = client.invoke(request)
    val rawJsonResponse = new String(result.getPayload.array(), "UTF-8")
    read(rawJsonResponse)
  }
}

This works fine when I have both input and outputs to the call, but can't figure out what's the best "Scala" way of dealing with when A or B should not be present.当我有调用的输入和输出时,这很好用,但是当 A 或 B 不应该存在时,无法弄清楚什么是最好的“Scala”处理方式。 I was looking for ways of getting the runtime type of A or B, checking against Unit, then base the logic on that, but couldn't find an obvious way (probably due to type erasure?)我一直在寻找获取 A 或 B 的运行时类型的方法,检查 Unit,然后基于该逻辑,但找不到明显的方法(可能是由于类型擦除?)

If there is a different pattern I can apply here, without generic types but with Optionals, or anything else which achieves the same thing, that's also great.如果我可以在这里应用不同的模式,没有泛型类型但有 Optionals,或者其他任何可以实现相同目的的东西,那也很棒。

Here is a simplified example of how you could use typeclass pattern.这是一个如何使用类型类模式的简化示例。 I'm going to define a local one that is responsible for both writing and reading an object我将定义一个负责写入和读取对象的本地对象

object LambdaInvoker {
  trait RW[A] {
    def addPayload(a: A, r: InvokeRequest): InvokeRequest
    def readResponse(r: InvokeResult): A
  }

Now, we need to define some instances (as implicits) with actual implementations.现在,我们需要使用实际实现定义一些实例(作为隐式)。 We have two cases: either it's Unit, and then we know how to handle that specifically, or it's something JSON-encodable.我们有两种情况:要么是 Unit,然后我们知道如何具体处理它,要么是 JSON 可编码的东西。

To make everything work, we would have to use implicit prioritization technique of putting a fallback into a separate trait/class that we extend from.为了使一切正常,我们必须使用隐式优先级技术将回退放入我们扩展的单独特征/类中。 Here's a way to handle Unit by not doing anything at all:这是一种完全不做任何事情来处理Unit的方法:

  object RW extends RWFallback {
    // special cases go into this object
    implicit val unitRW: RW[Unit] = new RW[Unit] {
      def addPayload(a: Unit, r: InvokeRequest) = r
      def readResponse(r: InvokeResult) = ()
    }
  }

And our fallback will be using json4s.我们的后备将使用 json4s。 Json4s requires something called a Manifest , which is a part of a legacy scala reflection API, and we can require just that, without limiting ourselves to AnyRef . Json4s 需要一个叫做Manifest东西,它是遗留 scala 反射 API 的一部分,我们可以只需要它,而不限制我们自己使用AnyRef

  trait RWFallback {
    // fallback to json4s if not special cased
    implicit def fallbackRW[A: Manifest]: RW[A] = new RW[A] {
      implicit val serialization: Serialization.type = org.json4s.native.Serialization
      implicit val formats = org.json4s.DefaultFormats

      def addPayload(a: A, r: InvokeRequest) = r.withPayload(write(a))
      def readResponse(r: InvokeResult) = read(new String(r.getPayload.array(), "UTF-8"))
    }
  }

This all has been inside object LambdaInvoker , since it's limited to only LambdaInvoker stuff.这一切都在object LambdaInvoker ,因为它仅限于 LambdaInvoker 的东西。

}

Now, for the implementation, you have to do the following:现在,为了实现,您必须执行以下操作:

  • Require instances of RW for participating datatypes (done using implicit parameter list, or context bound syntax, eg A: LambdaInvoker.RW )需要参与数据类型的 RW 实例(使用隐式参数列表或上下文绑定语法完成,例如A: LambdaInvoker.RW
  • Delegate to them in points where logic will vary depending on the type:在逻辑会因类型而异的地方委托给他们:
class LambdaInvoker[A: RW, B: RW](val client: AWSLambda = AWSLambdaClientBuilder.defaultClient()) {
  // "summon" the implicit values and give them a name, so we can refer to them
  private[this] val rwA = implicitly[RW[A]]
  private[this] val rwB = implicitly[RW[B]]

  def call(input: A, function: String): B = {
    // delegate serialization
    val request = rwA.addPayload(input, new InvokeRequest().withFunctionName(function))
    val result = client.invoke(request)
    // delegate parsing
    rwB.readResponse(result)
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM