2013-10-17 44 views
1

我有一個抽象類(這是一個類似的例子)Printer它打印提供的參數,除非它無法通過過濾器。 Printer是逆變w.r.t.其通用類型爲T超類型的Scala複製方法

class Printer[-T] { 
    val filters: Seq[Function2[T => Boolean]] 

    def print(t: T): Unit { 
    if (filters.forall(_(t))) doPrint(t) 
    } 
    def doPrint(t: T): Unit 
} 

我現在有Printer 20子類 - 一個對於字符串,整型,等等。由於Printer是逆變的,filters必須爲val。但是,如果我想要一個Printer的方法來添加一個過濾器,它需要是不可變的。

def addFilter[TT <: T](t: TT): Printer[TT] 

不幸的是我現在需要在我的20個子類中的每一箇中實現這個方法。有沒有辦法解決?

更新:另外,在addFilter,我不知道如何返回子類而不是超類Printer。例如,如果我在StringPrinter上調用addFilter,理想情況下我會返回StringPrinter這一類型。

回答

1

以下是您對Printer進行編碼的方式的一些偏離,但可能會實現您的意圖。請注意,您的編碼類的這種方式能夠關注一個更大的分離:您可以定義獨立的是如何使用它們的過濾器和打印實現(在doPrint參數):

case class Printer[T](val filters: List[Function1[T, Boolean]], val doPrint: T => Unit) { 
    def print(t: T): Unit = { 
    if (filters.forall(_(t))) doPrint(t) 
    } 

    def addFilter[TT <: T](f: TT => Boolean): Printer[TT] = copy(f :: filters) 
} 

請注意,我沒有必要來在此指定違約性,不確定這是否會成爲您的問題。

使用類,你並不需要繼承,只是通過適當的參數到構造函數(實際上,提供的同伴apply工廠方法免費爲case類) - 例如:

case class Foo(x: Int) 
val fooChk1: Foo => Boolean = (f: Foo) => f.x != 1 
val fooPrinter1 = Printer(fooChk1 :: Nil, (f: Foo) => println(s"Foo: x = ${f.x}")) 

val fooChk3: Foo => Boolean = (f: Foo) => f.x != 3 
val fooPrinter2 = fooPrinter1.addFilter(fooChk3) 

val foo3 = Foo(3) 
fooPrinter1.print(foo3) // Prints 'Foo: x = 3' 
fooPrinter3.print(foo3) // Prints nothing 

有對於參數Print(例如將構造函數更改爲(val filters: List[Function1[T, Boolean]])(implicit val doPrint: T => Unit))以及對於特定的Print[X]變體也是適用於此處的含義的適當範圍。

+0

很好的例子。我不認爲這對我很有用。我使用子類,因爲功能通常非常複雜,我使用反射創建DSL中指定的子類的實例。 – schmmd

+0

更多地思考它,關注的分離可能是我的問題。也許我會製作一個'ConstraintedPrinter [T](printer:Printer [T],filters:Seq [T => Boolean])''。 – schmmd