2017-02-13 48 views
0

在很多語言中做一件簡單的事情,但在Scala中沒有做的事情是:在Scala中,如何對伴隨對象執行編譯時類型檢查?

定義原型'超級',這樣'超級'的所有實現都必須定義一個構造函數'create()'。

我發現這個約束非常重要,能夠在運行前識別很多問題。然而,這個特性只在Java中部分強制執行(通過定義一個總是拋出錯誤的'抽象'靜態方法),並且在Scala中完全缺失(伴隨對象完全脫離類並且不能在原型中強制執行)。

有沒有一個宏或工具,允許我這樣做?

UPDATE對不起,我的問題缺少上下文和示例。這裏是一個正式的用例中階:

在項目中,我們定義了可以被所有的子項目進行擴展接口:

trait AbstractFoo {} 

這個接口應該始終有一個默認的0參數的構造/構造,所以項目A可以初始化點播,但是,每一個構造函數的實現是未知的項目答:

object AbstractFoo { 

    def default[T <: AbstractFoo: ClassTag](): T 
} 

所以,問題就變成了:如何嚴格地定義AbstractFoo,使得對A,任何的各子項目AbstractFoo的實現:

case class Foo(...) extends AbstractFoo 

必須滿足:

  1. '富' 必須定義(大概在其同伴對象)0-參數助洗劑/構造

  2. 主叫AbstractFoo.defaultFoo可以調用此0 - 參數生成器/構造器

應該指出的是,條件下,溶液中存在,其是定義每一個同伴對象作爲隱式類型類:

trait FooBuilder[T <: AbstractFoo] { 
    def default(): T 
} 

object AbstractFoo { 

    implicit object Foo extends FooBuilder[Foo] { 
    def default() = {...} 
    } 

    def default[T <: AbstractFoo: FooBuilder](): T = { 
    implicitly[FooBuilder[T]].default 
    } 
} 

使得如果隱式對象是未定義編譯器會給出一個隱式的未找到錯誤(我的代碼段可具有一些語法錯誤,這個想法來自http://www.cakesolutions.net/teamblogs/demystifying-implicits-and-typeclasses-in-scala

不幸的是,這並不總是很方便,因爲A的這個子項目通常對項目A是未知的。然而,默認的隱式構建器不能被重新定義,這使得每個default()調用都更加複雜。

我相信scala是一種非常易於擴展的語言,所以無論使用宏,註釋還是其他元編程技術,至少應該有一種方法來執行它。我的問題現在清楚了嗎?

UPDATE2:我相信我發現後的解決方案仔細研究Scaladoc,還有隱藏在角落裏的註釋:

如果有匹配的隱含參數的類型,最具體的人會這幾個符合條件的參數使用靜態重載分辨率的規則(見Scala的規範§6.26.4)來選擇:

...

的類型參數隱範圍(2.8.0)

...

因此,所有我需要的是寫FooBuilder的隱函數:

trait FooBuilder[T <: AbstractFoo] { 
    def default(): T 

    implicit def self = this 
} 

object Foo extends FooBuilder[Foo] 

所以每次有人叫:

default[Foo] 

斯卡拉將引用類的範圍Foo包含對象Foo,它包含隱式值Foo,並最終找到0參數構造函數。

我認爲這個定義比在對象FooBuilder中定義它要好,因爲你只能定義一次FooBuilder,因此它不是可擴展的。你會同意我嗎?如果是這樣,你能否修改你的答案,以便我可以授予你的觀點?

+1

我不認爲'抽象'靜態方法是Java中的一件事情。靜態方法不能被覆蓋。 –

+1

歐普是如此無知,但他確信他說的有點有趣...我不知道他是否在嘲弄 –

+0

不,這是一個合理的問題(請參閱@Wheaties的回答中的評論) – Phasmid

回答

4

我不明白爲什麼abstract class甚至Trait不會允許這樣做?

abstract class DefineCreate{ 
    def create(): Unit 
} 

case class Foo(one: Int) 
object Foo extends DefineCreate{ 
    def create(): Unit = { Console.out.println("side-effect") } 
} 

因此我強迫用戶做出的object一個create方法有問題,因爲DefineCreate所有的實現必須以編譯這樣做。

更新如下意見

好了,而不必訴諸於宏之類的,你可以實現同樣的事情與類型類:

trait Constructor[A]{ 
    def create(): A 
} 

object Construct{ 
    def create[A](implicit cr: Constructor[A]): A = cr.create() 
} 

這並不明確強制伴隨對象來萌芽方法,但如果他們想要使用Constructor.create[Foo]模式,它會強制用戶創建類型類。

+0

那麼你是如何1.要求Foo的所有子類必須在其伴侶對象中定義一個create()? 2.通過類型info Class [Foo]找到函數Foo.create()? – tribbloid

+2

啊,現在我明白你在問什麼了。你有這個用例嗎?我不明白你爲什麼需要它。 – Phasmid

+0

點2不是你通常在Scala中做的事情。 –