2013-12-08 61 views
1

我面临着以下类型类的一些奇怪的问题:由于某种原因,隐式对象ContentUploader没有呼叫决心上传DemoActor方法。斯卡拉,类型类和“找不到隐含价值”

import akka.actor.Actor 
import java.io.File 
import org.slf4j.LoggerFactory 

class DemoActor extends Actor { 

    import DemoActor.UploaderImpl._ 

    override def receive = { 
    case (x: DemoActor.Content) => 
     DemoActor.upload(x) 
    } 

} 

object DemoActor { 

    val LOG = LoggerFactory.getLogger("DemoActor") 

    sealed trait UploadData { 
    val data: Array[File] 
    } 

    case class Content(data: Array[File]) extends UploadData 

    case class UploadResult(url: String, contentType: String, size: Long) 

    trait S3Uploader[T <: UploadData] { 

    def uploadToS3(filez: Array[File]): Iterable[UploadResult] 

    } 

    object UploaderImpl { 

    val LOG = LoggerFactory.getLogger("Uploader") 

    private def contentType(name: String): String = { 
     "application/octet-stream" 
    } 

    private def doUpload(filez: Array[File], bucketName: String) = { 
     LOG.debug("Uploading: {} to {}", filez, bucketName) 
     filez.flatMap { 
     case f => 
      try { 
      val key = f.getName 
      val mime = contentType(f.getName) 
      Some(UploadResult("http://" + bucketName + ".s3.amazonaws.com/" + key, mime, f.length())) 
      } catch { 
      case e => 
       LOG.error("Can not upload", e) 
       None 
      } 
     } 
    } 

    implicit object ContentUploader extends S3Uploader[Content] { 

     lazy val bucketName = "resources.aws.bucketname" 

     lazy val awsSecret = "resources.aws.secret.key" 

     lazy val awsAccess = "resources.aws.access.key" 

     override def uploadToS3(filez: Array[File]) = doUpload(filez, bucketName) 

    } 

    } 

    def upload[T <: UploadData](src: T)(implicit uploader: S3Uploader[T]) = uploader.uploadToS3(src.data) 


} 

我在这里错过了什么?

UPD

如果我搬到类DemoActor的定义里面对象 DemoActor,像

import akka.actor.Actor 
import java.io.File 
import org.slf4j.LoggerFactory 

object DemoActor { 

    val LOG = LoggerFactory.getLogger("DemoActor") 

    sealed trait UploadData { 
    val data: Array[File] 
    } 

    case class Content(data: Array[File]) extends UploadData 

    case class UploadResult(url: String, contentType: String, size: Long) 

    trait S3Uploader[UploadData] { 

    def uploadToS3(filez: Array[File]): Iterable[UploadResult] 

    } 

    object UploaderImpl { 

    val LOG = LoggerFactory.getLogger("Uploader") 

    private def contentType(name: String): String = { 
     "application/octet-stream" 
    } 

    private def doUpload(filez: Array[File], bucketName: String) = { 
     LOG.debug("Uploading: {} to {}", filez, bucketName) 
     filez.flatMap { 
     case f => 
      try { 
      val key = f.getName 
      val mime = contentType(f.getName) 
      Some(UploadResult("http://" + bucketName + ".s3.amazonaws.com/" + key, mime, f.length())) 
      } catch { 
      case e => 
       LOG.error("Can not upload", e) 
       None 
      } 
     } 
    } 

    implicit object ContentUploader extends S3Uploader[DemoActor.Content] { 

     lazy val bucketName = "resources.aws.bucketname" 

     lazy val awsSecret = "resources.aws.secret.key" 

     lazy val awsAccess = "resources.aws.access.key" 

     override def uploadToS3(filez: Array[File]) = doUpload(filez, bucketName) 

    } 

    } 

    def upload[T <: UploadData](src: T)(implicit uploader: S3Uploader[T]) = uploader.uploadToS3(src.data) 

    class DemoActor extends Actor { 

    import DemoActor.UploaderImpl._ 

    override def receive = { 
     case (x: DemoActor.Content) => 
     DemoActor.upload(x) 
    } 

    } 


} 

则一切正常。有没有命名空间的问题?

回答

5

它没有找到它,因为隐式前向引用必须明确键入才能被考虑,而这不是。

如果这是令人困惑的,也许有两种解决方法可能会说清楚。首先,你可以声明隐式的类型。除去来自物体的implicit,并声明一个val指向它:

implicit val contentUploader: S3Uploader[DemoActor.Content] = ContentUploader 

第二种方式移动的class DemoActor声明的文件的末尾,所以它的所述object DemoActor声明之后保持。

它这样工作的原因是,编译器必须在文件的其余部分完全键入之前搜索隐式,所以它当时不知道该对象ContentUploader满足搜索。

+0

确实有道理,谢谢!所以一般情况下,应该在使用它们之前定义它们。 – jdevelop

+0

@jdevelop我更喜欢总是明确地声明它们的类型。 –