Scala Generics - 重载方法

时间:2014-12-23 22:31:19

标签: spring scala generics spring-data aspectj

考虑给定代码:

    val repository =
      context.getBean(
        Introspector.decapitalize(t.getClass.getSimpleName).replace("C", "E").concat("Repository"))

并且我的存储库有一个String as Serializable。

我正在尝试执行以下操作:

repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t))

这个很好用:

repository.asInstanceOf[ElasticsearchRepository[_, String]].findAll()

但我不知道如何将上述内容付诸实践。

假设方法getObject(t)正在重新调整要保留的正确对象,并且因为它是一个Spring Data Repository,所以有2个save方法。一个接受单个实体,另一个接受实体列表,它表示重载方法值保存。

到目前为止我尝试过:

我在另一个线程中看到强制使用类型的方法,如下所示:

repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t) : TYPE)

如果我知道类型,并且我的方法getObject应返回相同的类型,这是可以的。

这是我的getObject方法,我返回对象本身没有任何特定的类型:

@throws[IOException]
  def getObject[T](t : T) = {
    objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t))
  }

所以我试图得到这样的类型:

val m = Manifest.classType(getClazz(t))
    type TYPE = m.type

如果我使用getObject(t) : TYPE强制我的对象使用此类型,但我不知道如何在我的getObject方法中使用相同的类型来返回。

无论如何,我甚至不知道这是否是执行此操作的最佳方法,调用通用存储库并保存通用对象。

只是为了理解我正在尝试做什么,我正在使用一个方面拦截一个Cassandra实体被持久化,然后得到它并变成一个ElasticSearch实体来保存一个json(这就是为什么getObject(t) )并复制到ElasticSearch。

以下是完整的方面类:

@Component
@Aspect
class ElasticAop {

  @Autowired val context : ApplicationContext = null

  val objectMapper : ObjectMapper = new ObjectMapper()

  @Pointcut("execution(* com.test.service.cassandra.*.post(..)) && args(t)")
  def getPointcutPost[T](t : T) : Unit = {}

  @throws[Throwable]
  @Before("getPointcutPost(t)")
  def elasticSaveAspect[T](joinPoint: JoinPoint, t: T) = {
    val m = Manifest.classType(getClazz(t))
    type TYPE = m.type

    val repository =
      context.getBean(
        Introspector.decapitalize(t.getClass.getSimpleName).replace("C", "E").concat("Repository"))

    repository.asInstanceOf[ElasticsearchRepository[_, String]].findAll()
    repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t))
  }

  @throws[ClassNotFoundException]
  def getClazz[T](t : T) = {
    val className = t.getClass.getName.replace("cassandra", "elastic").replace("C", "E")
    Class.forName(className)
  }

  @throws[IOException]
  def getObject[T](t : T) = {
    objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t))
  }

}

EDITED

甚至在我的getObject to Address中设置一个类型返回,然后设置save方法如下save(getObject(t):Address)给我同样的重载错误。

EDITED

我只知道这是一个限制,可能的工作是创建一个工厂或类似的东西。 然后我使用saveOrUpdate方法创建了一个服务:

trait ElasticGenericService[T <: ElasticGenericKey, R <: ElasticsearchRepository[T, String]] {

  var r : R = _

  def saveOrUpdate(t: T) = r.save(t)

}

现在我得到一个演员异常:

java.lang.ClassCastException: Address cannot be cast to scala.runtime.Nothing$

1 个答案:

答案 0 :(得分:1)

我在这里可以看到:

  • getObject[T](t : T)返回存在类型_1并实际杀死所有类型检查,因为您在运行时选择了类
  • ElasticsearchRepository[_, String].save要求将存在类型_2传递给保存方法,因此_1不适合

可能的解决方案:

repository.asInstanceOf[ElasticsearchRepository[Any, String]].save(getObject(t).asInstanceOf[Any]) //getClass will work with runtime class instead of Any, so should be fine

另一种解决方案(保存存在类型):

def getObject[T](t : T) = {
    objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t)).asInstanceOf[T]
} //assuming T is an existential - it will return same existential as you passed