2010-12-09 4 views
4

В TraversableOnce существует метод sum, который может использоваться только в том случае, если содержащийся тип Numeric (иначе он не будет компилироваться). Интересно, можно ли это использовать для другого случая (чтобы избежать проверки времени выполнения).Как сделать проверку типа во время компиляции?

В частности, в случае, когда мы имеем две черты A и B. Мы хотим иметь метод f, который может быть использован только в случае, если объект наследует как A и B. Но нет, если она распространяется только один из них. Я не хочу делать еще один trait AB extends A with B. Я просто хочу, чтобы не удалось использовать f, если оба свойства не наследуются.

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: решение, благодаря @Aaron Novstrup

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

Сейчас в main, b.f не компилируется. Хороший

EDIT 2: Измененная строка [1] [2] отражает изменения в ответ от @Aaron Novstrup

РЕДАКТИРОВАТЬ 3: без использования self отражать изменения в ответ от @Aaron Novstrup

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

Вы также можете использовать параметр `ev`, чтобы избежать приведения - это` Function1 [self.type, Foo] `. Обновленный мой ответ, чтобы продемонстрировать. – 2010-12-10 01:37:53

ответ

8

Да, вы можете:

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!") 
    } 
} 

компилятор только будет в состоянии поставить параметр evidence, если статический тип объекта (в месте вызова) продолжается A.

+0

Ницца, он работает, спасибо. В первый раз я вижу, что я не использовал силу цепи наследования, но предлагаю улучшения. И, следовательно, с первого раза я не могу обойтись. – shellholic 2010-12-10 01:34:35

+1

На самом деле, тип типа не нужен. Вы можете просто использовать `this`. – 2010-12-10 01:40:07

3

Зная, что сигнатура суммы является

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

Вы, кажется, предполагая, что число типов распространяется Numeric, это не так. На самом деле они неявно преобразуются в Числовой, в случае Int неявный используется scala.math.Numeric.IntIsIntegral, которые определяют операции, такие как плюс и раз.

Таким образом, ограничение на какие типы TraversableOnce [А] .sum допускается достигается за счет существования неявной provinding необходимые операции.

Это просто краткое объяснение общей работы классов Numeric и type. Для большей проверки источников математики. Нумер. XisY, математика. Интеграл и математика. Фрагмент и как работают классы типов: implicit-tricks-type-class-pattern и type-class-pattern-example.

Смежные вопросы