2010-12-09 5 views
4

Dans TraversableOnce, il existe une méthode sum qui est uniquement utilisable si le type contenu est Numeric (sinon, il ne sera pas compilé). Je me demande si cela est utilisable pour d'autres cas (pour éviter la vérification de l'exécution).Comment effectuer la vérification de type lors de la compilation?

En particulier le cas où nous avons deux traits A et B. Nous voulons avoir une méthode f qui peut être utilisé que si l'objet hérite à la fois A et B. Mais pas si elle ne couvre que l'un d'entre eux. Je ne veux pas faire un autre trait AB extends A with B. Je veux juste être incapable d'utiliser f si les deux traits ne sont pas hérités.

package com.example 

trait Base 
trait Foo extends Base { 
    def g = println("foo bar " + toString) 
} 
trait Bar extends Base { 
    /* If this is both Foo and Bar, I can do more */ 
    def f = { 
    if (!this.isInstanceOf[Foo]) error("this is not an instance of Foo") 
    this.asInstanceOf[Foo].g 
    } 
} 
object Test { 
    def main(args: Array[String]): Unit = { 
    object ab extends Foo with Bar 
    object ba extends Bar with Foo 
    object b extends Bar 
    ab.f 
    ba.f 
    // I don't want next line to compile: 
    try { b.f } catch { case e: RuntimeException => println(e) } 
    } 
} 

EDIT: solution, grâce à Novstrup @ Aaron

trait Bar extends Base { self => 
    def f(implicit ev: self.type <:< Foo) = { 
    //self.asInstanceOf[Foo].g // [1] 
    ev(this).g // [2] 
    } 
} 

Maintenant dans main, b.f ne compile pas. Belle

EDIT 2: ligne modifiée [1] [2] refléter les changements dans la réponse par Novstrup @ Aaron

EDIT 3: sans utiliser self refléter les changements dans la réponse par Novstrup @ Aaron

trait Bar extends Base { 
    /* If this is both Foo and Bar, I can do more */ 
    def f(implicit ev: this.type <:< Foo) = { 
    ev(this).g 
    } 
} 
+1

Vous pouvez également utiliser le paramètre 'ev' pour éviter le cast - c'est un' Function1 [self.type, Foo] '. Mise à jour ma réponse à démontrer. –

Répondre

8

Oui, vous pouvez:

trait A { 
    def bar = println("I'm an A!") 
} 

trait B { 
    def foo(implicit ev: this.type <:< A) = { 
     ev(this).bar 
     println("and a B!") 
    } 
} 

le compilateur ne sera en mesure de fournir le paramètre evidence si le type statique (sur le site d'appel) de l'objet s'étend A.

+0

Bien, ça marche, merci. La première fois que je vois un moi non utilisé pour forcer une chaîne d'héritage, mais pour suggérer une amélioration. Et par conséquent, la première fois que je ne peux pas faire sans. – shellholic

+1

En fait, le type auto n'est pas nécessaire. Vous pouvez simplement utiliser 'this'. –

+0

Est-ce que A ne devrait pas s'appliquer (x: B) pour que cela fonctionne? – pedrofurla

3

Sachant que la signature de somme est

def sum [B >: A] (implicit num: Numeric[B]) : B 

Vous semblez supposer que les types de nombre étend Numeric, ce n'est pas vrai. En fait, ils sont convertis implicitement à numérique, dans le cas de Int l'occasion implicite est scala.math.Numeric.IntIsIntegral qui définissent les opérations comme , plus et fois.

Ainsi, la restriction sur les types de A un TraversableOnce [A] .sum est autorisé est atteint par l'existence d'implicite provinding les opérations nécessaires.

Ceci est juste une explication rapide du fonctionnement général des classes numériques et de type. Pour plus de vérifier les sources de math.Numeric.XisY, math.Integral et math.Fractional et comment les classes de caractères fonctionne: implicit-tricks-type-class-pattern et type-class-pattern-example.

+0

Ce n'est pas correct, mais c'est proche. Il n'y a pas de conversion implicite de 'Int' en' Numeric [Int] ', mais il y a un objet' Numeric [Int] 'implicite dans la portée (' IntIsIntegral') qui permet au compilateur de fournir le paramètre implicite '' méthode sum'. –

+0

@Aaron, vous avez absolument raison. Fixé. – pedrofurla

Questions connexes