隐式转换与隐式参数

时间:2012-02-17 10:49:31

标签: scala

我正在使用Object参数实现一个带有很多方法的Java接口,在我的例子中,它实际上是包含用户名的字符串:

public interface TwoFactorAuthProvider {
    boolean requiresTwoFactorAuth(Object principal);
    ... //many more methods with the same kind of parameter
}

我正在尝试使用隐式转换将这些转换为我的实现中的User个对象:

class TwoFactorAuthProviderImpl(userRepository: UserRepository) 
    extends TwoFactorAuthProvider {

    def requiresTwoFactorAuth(user: User): Boolean = {
        ...
    }
}

当我在我的班级的伴侣对象中定义转换时,它被拾起就好了,我的班级编译:

object TwoFactorAuthProviderImpl {
    implicit def toUser(principal: Any): User = {
        null //TODO: do something useful
    }
}

但是,为了能够进行转换,我需要访问TwoFactorAuthProviderImpl实例所拥有的用户存储库,但是配对对象却没有。我以为我可以使用隐式参数传递它:

implicit def toUser(principal: Any)(implicit repo: UserRepository): User = {
    val email = principal.asInstanceOf[String]
    repo.findByEmail(email)
}

但是使用隐式参数,编译器不再接收转换(抱怨我没有实现接口)。

有没有办法获得我想要的隐式转换,或者这是否超出了你可以用implicits做的范围?

2 个答案:

答案 0 :(得分:4)

这应该可以正常工作 - 你能提供确切的编译错误吗?没有实现什么接口?您似乎必须声明如下:

class TwoFactorAuthProviderImpl(implicit userRepository: UserRepository) 

以下是REPL的一个示例,表明implicits可能有隐含;我正在使用粘贴模式来确保module Xclass X

的伴随对象
scala> :paste
// Entering paste mode (ctrl-D to finish)

case class X(i: Int, s: String)
object X { implicit def Int_Is_X(i: Int)(implicit s: String) = X(i, s) }

// Exiting paste mode, now interpreting.

defined class X
defined module X

scala> val i: X = 4
<console>:9: error: value X is not a member of object $iw
       val i: X = 4
                  ^

但是如果我们在范围

中添加隐式字符串
scala> implicit val s = "Foo"
s: java.lang.String = Foo

scala> val i: X = 4
i: X = X(4,Foo)

暗示建议

不要过度使用隐式转换 - 我认为你在这个意义上走得太远 - 主体隐含地是一种机制,通过它你可以发现用户,它不是隐含的用户本身。我很想做这样的事情:

implicit def Principal_Is_UserDiscoverable(p: String) = new {
  def findUser(implicit repo: UserRepository) = repo.findUser(p)
}

然后你可以"oxbow".findUser

答案 1 :(得分:1)

感谢Oxbow's answer,我现在有了它的工作,这仅供参考。

首先,应该作为隐式传递的值本身必须标记为隐式:

class TwoFactorAuthProviderImpl(implicit userRepository: UserRepository) ...

其次,隐式转换很好,但是方法实现签名仍然必须与其声明的签名相匹配。因此,即使存在从AnyUser的转换:

,也无法编译
def requiresTwoFactorAuth(principal: User): Boolean = { ... }

但是将参数保留为Any,如声明中那样,然后将其作为用户使用就可以了:

def requiresTwoFactorAuth(principal: Any): Boolean = {
    principal.getSettings().getPhoneUsedForTwoFactorAuthentication() != null
}

此外,在这种情况下,转换实际上不必在伴随对象中,所以最后,我将隐式参数保留了。

完整的源代码:

class TwoFactorAuthProviderImpl(userRepository: UserRepository) 
    extends TwoFactorAuthProvider  {

    private implicit def toUser(principal: Any): User = {
        val email = principal.asInstanceOf[String]
        userRepository.findByEmail(email)
    }

    def requiresTwoFactorAuth(principal: Any): Boolean = {
        //using principal as a User
        principal.getSettings().getPhoneUsedForTwoFactorAuthentication() != null
    }

    ...
}