Il est possible d'obtenir ce que vous recherchez, mais ce n'est pas simple. Le problème est que sans un paramètre de type explicite, le compilateur peut seulement déduire que T
est Nothing
. Dans ce cas, vous voulez find
pour retourner quelque chose de type Node
, pas de type T
(à savoir Nothing
), mais dans tous les cas, vous voulez trouver quelque chose pour revenir de type T
.
Lorsque vous souhaitez que votre type de retour varie en fonction d'un paramètre de type, vous pouvez utiliser une technique similaire à celle que j'ai utilisée dans method lifting API.
object Finder {
def find[T <: Node] = new Find[T]
class Find[T <: Node] {
def apply[R](name: String)(implicit e: T ReturnAs R): R =
doFind(name).asInstanceOf[R]
}
sealed class ReturnAs[T, R]
trait DefaultReturns {
implicit def returnAs[T] = new ReturnAs[T, T]
}
object ReturnAs extends DefaultReturns {
implicit object returnNothingAsNode extends ReturnAs[Nothing, Node]
}
}
Ici, le procédé find
retourne un foncteur polymorphe qui, lorsqu'elle est appliquée à un nom, retourne un objet de chaque type ou de type T
Node
en fonction de la valeur de l'argument ReturnAs
fourni par le compilateur. Si T
est Nothing
, le compilateur fournira l'objet returnNothingAsNode
et la méthode apply retournera un Node
. Sinon, le compilateur fournira un ReturnAs[T, T]
et la méthode apply retournera un T
.
Riffing au large de la solution de Paul sur la liste de diffusion, une autre possibilité est de fournir un implicite pour chaque type qui « fonctionne ». Au lieu de retourner un Node
lorsque le paramètre de type est omis, une erreur de compilation sera émis:
object Finder {
def find[T : IsOk](name: String): T =
doFind(name).asInstanceOf[T]
class IsOk[T]
object IsOk {
implicit object personIsOk extends IsOk[Person]
implicit object nodeIsOk extends IsOk[Node]
}
}
Bien sûr, cette solution n'échelle pas bien.
@Ben, thx pour votre correction à mon post initial bâclée :-) –