1

I'm implementing a Java interface with a lot of methods with Object parameters, which in my case are really Strings containing user names:

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

I'm trying to use implicit conversion to convert these to User objects in my implementation:

class TwoFactorAuthProviderImpl(userRepository: UserRepository) 
    extends TwoFactorAuthProvider {

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

When I define the conversion in the companion object of my class, it is picked up just fine and my class compiles:

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

However, to be able to do the conversion, I need access to the user repository, which the TwoFactorAuthProviderImpl instance has, but the companion object does not. I thought I could possibly use an implicit parameter to pass it:

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

But with the implicit parameter, the conversion is no longer picked up by the compiler (complaining that I'm not implementing the interface).

Is there a way to get the implicit conversion that I want, or is this outside the scope of what you can do with implicits?

2 Answers 2

4

This should work just fine - can you supply the exact compilation error? Not implementing what interface? It looks like you would have to declare as follows:

class TwoFactorAuthProviderImpl(implicit userRepository: UserRepository) 

Here's an example for the REPL to show that implicits can have implicits; I'm using paste mode to ensure that module X is the companion object of the class 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
                  ^

But if we add an implicit string in scope

scala> implicit val s = "Foo"
s: java.lang.String = Foo

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

Implicits advice

Don't go overboard with implicit conversions - I think you are going too far in this sense - the principal is implicitly a mechanism by which you can discover a user, it is not implicitly a user itself. I'd be tempted to do something like this instead:

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

Then you can do "oxbow".findUser

Sign up to request clarification or add additional context in comments.

6 Comments

The compiler says "class TwoFactorAuthProviderImpl needs to be abstract, since: method requiresTwoFactorAuth in trait TwoFactorAuthProvider of type (x$1: Any)Boolean is not defined (Note that Any does not match com.ontopofthings.usermanagement.entities.User)".
userRepository should be picked up, as is a member of TwoFactorAuthProviderImpl, right?
It's not implicit though. If a parameter is marked as being implicit, then only things marked as implicit can be implicitly sent to it. As for the compiler error, unless you post a short piece of code demonstrating the exact problem in isolation, it's really difficult for us to tell. The answer to the question "Can implicits have implicits?" is a resounding "yes". The answer to "What is wrong with my code?" is "I'm not sure, you have not posted enough for me to tell"
PS - note my edit. I think if you mark the userRepository parameter as implicit, it will get passed through into your implicit lookup as desired
Thanks! Indeed I had to mark userRepository as implicit to have it passed to the conversion. Also, the parameters of my functions have to remain principal: Any, but thanks to the conversion I can then use them as though they were Users. I'll post a summary of the working code in my answer.
|
1

Thanks to Oxbow's answer, I now have it working, this is only for reference.

First of all, a value that should be passed as an implicit must itself be marked implicit:

class TwoFactorAuthProviderImpl(implicit userRepository: UserRepository) ...

Second, implicit conversions are nice and all, but a method implementation signature must still match the signature of its declaration. So this does not compile, even though there is a conversion from Any to User:

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

But leaving the parameter as Any, as in the declaration, and then using it as a user works just fine:

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

Also, the conversion really doesn't have to be in the companion object in this case, so in the end, I left the implicit parameters out.

The full source code:

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
    }

    ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.