2012-12-02 125 views
30

我只是想知道是否可以在Scala中重複密封特徵? 如果沒有,爲什麼不可能?既然這個特質是密封的,它應該是不可能的?斯卡拉密封特徵的迭代?

我想要做的就是這樣的事情:

sealed trait ResizedImageKey { 

    /** 
    * Get the dimensions to use on the resized image associated with this key 
    */ 
    def getDimension(originalDimension: Dimension): Dimension 

} 

case class Dimension(width: Int, height: Int) 

case object Large extends ResizedImageKey { 
    def getDimension(originalDimension: Dimension) = Dimension(1000,1000) 
} 

case object Medium extends ResizedImageKey{ 
    def getDimension(originalDimension: Dimension) = Dimension(500,500) 
} 

case object Small extends ResizedImageKey{ 
    def getDimension(originalDimension: Dimension) = Dimension(100,100) 
} 

我想可以在Java通過給一個實現枚舉值來完成。在Scala中是否有相同的內容?

+1

不是[這](https://gist.github.com/ea5e46a2f392204993fa)你想要什麼? –

+0

謝謝!試圖瞭解爲什麼我不能使用案件對象;) –

回答

50

這實際上是在我的意見是一個適合2.10宏的用例:你想獲得你知道編譯器有的但沒有公開的信息,並且宏給你一個(合理)簡單的方法來窺視內部。見我的回答here的相關(不過現在稍微外的日期),例如,或者只是使用這樣的事情:

import language.experimental.macros 
import scala.reflect.macros.Context 

object SealedExample { 
    def values[A]: Set[A] = macro values_impl[A] 

    def values_impl[A: c.WeakTypeTag](c: Context) = { 
    import c.universe._ 

    val symbol = weakTypeOf[A].typeSymbol 

    if (!symbol.isClass) c.abort(
     c.enclosingPosition, 
     "Can only enumerate values of a sealed trait or class." 
    ) else if (!symbol.asClass.isSealed) c.abort(
     c.enclosingPosition, 
     "Can only enumerate values of a sealed trait or class." 
    ) else { 
     val children = symbol.asClass.knownDirectSubclasses.toList 

     if (!children.forall(_.isModuleClass)) c.abort(
     c.enclosingPosition, 
     "All children must be objects." 
    ) else c.Expr[Set[A]] { 
     def sourceModuleRef(sym: Symbol) = Ident(
      sym.asInstanceOf[ 
      scala.reflect.internal.Symbols#Symbol 
      ].sourceModule.asInstanceOf[Symbol] 
     ) 

     Apply(
      Select(
      reify(Set).tree, 
      newTermName("apply") 
     ), 
      children.map(sourceModuleRef(_)) 
     ) 
     } 
    } 
    } 
} 

現在我們可以寫:

scala> val keys: Set[ResizedImageKey] = SealedExample.values[ResizedImageKey] 
keys: Set[ResizedImageKey] = Set(Large, Medium, Small) 

這是所有的都非常安全 - 如果你問一個沒有密封的類型的值,有非對象的孩子等,你會得到一個編譯時錯誤。

+0

尼斯:)它不像我們在Java中那樣容易,但它會完成工作(當我將使用2.10 ...) –

+5

是的,Java的'enum'可以說比Scala的'Enumeration'少一個火車殘骸,並且在這方面比密封特性更方便,但是我仍然會選擇類似於Scala的類似於enum的方法一週中的天。 –

+5

實際上這個宏中有一個錯誤,如http://stackoverflow.com/questions/18732362/issue-with-using-macros-in-sbt突出顯示。它的最後一行應該替換爲https://gist.github.com/xeno-by/6573434。 –

3

本地沒有這種能力。在更常見的情況下,這並不合適,在這種情況下,您將實際的類作爲密封特徵的子類,而不是實例類對象。它看起來像你的情況可能是由枚舉來更好地處理

object ResizedImageKey extends Enumeration { 
    type ResizedImageKey = Value 
    val Small, Medium, Large = Value 
    def getDimension(value:ResizedImageKey):Dimension = 
     value match{ 
     case Small => Dimension(100, 100) 
     case Medium => Dimension(500, 500) 
     case Large => Dimension(1000, 1000) 

} 

println(ResizedImageKey.values.mkString(",") //prints Small,Medium,Large 

或者,你可以自己創建一個枚舉,可能將其放置在同伴對象爲方便

object ResizedImageKey{ 
    val values = Vector(Small, Medium, Large) 
} 

println(ResizedImageKey.values.mkString(",") //prints Small,Medium,Large 
+0

謝謝。這就是我所做的:創建一個值列表 –

0

也可以解決問題的東西是添加一個隱式轉換來將方法添加到枚舉中,而不是iteraring ov呃密封的特質。

object SharingPermission extends Enumeration { 
    val READ = Value("READ") 
    val WRITE = Value("WRITE") 
    val MANAGE = Value("MANAGE") 
} 


/** 
* Permits to extend the enum definition and provide a mapping betweet SharingPermission and ActionType 
* @param permission 
*/ 
class SharingPermissionExtended(permission: SharingPermission.Value) { 

    val allowRead: Boolean = permission match { 
    case SharingPermission.READ => true 
    case SharingPermission.WRITE => true 
    case SharingPermission.MANAGE => true 
    } 
    val allowWrite: Boolean = permission match { 
    case SharingPermission.READ => false 
    case SharingPermission.WRITE => true 
    case SharingPermission.MANAGE => true 
    } 
    val allowManage: Boolean = permission match { 
    case SharingPermission.READ => false 
    case SharingPermission.WRITE => false 
    case SharingPermission.MANAGE => true 
    } 

    def allowAction(actionType: ActionType.Value): Boolean = actionType match { 
    case ActionType.READ => allowRead 
    case ActionType.WRITE => allowWrite 
    case ActionType.MANAGE => allowManage 
    } 

} 

object SharingPermissionExtended { 
    implicit def conversion(perm: SharingPermission.Value): SharingPermissionExtended = new SharingPermissionExtended(perm) 
} 
8

上面提到的基於Scala宏的解決方案效果很好。然而,它不喜歡的情況:

sealed trait ImageSize        
object ImageSize {         
    case object Small extends ImageSize    
    case object Medium extends ImageSize    
    case object Large extends ImageSize    
    val values = SealedTraitValues.values[ImageSize] 
}             

要實現這一點,可以使用此代碼:

import language.experimental.macros 
import scala.reflect.macros.Context 

object SealedExample { 
    def values[A]: Set[A] = macro values_impl[A] 

    def values_impl[A: c.WeakTypeTag](c: Context) = { 
     import c.universe._ 

     val symbol = weakTypeOf[A].typeSymbol 

     if (!symbol.isClass) c.abort(
      c.enclosingPosition, 
      "Can only enumerate values of a sealed trait or class." 
     ) else if (!symbol.asClass.isSealed) c.abort(
      c.enclosingPosition, 
      "Can only enumerate values of a sealed trait or class." 
     ) else { 
      val siblingSubclasses: List[Symbol] = scala.util.Try { 
       val enclosingModule = c.enclosingClass.asInstanceOf[ModuleDef] 
       enclosingModule.impl.body.filter { x => 
        scala.util.Try(x.symbol.asModule.moduleClass.asClass.baseClasses.contains(symbol)) 
         .getOrElse(false) 
       }.map(_.symbol) 
      } getOrElse { 
       Nil 
      } 

      val children = symbol.asClass.knownDirectSubclasses.toList ::: siblingSubclasses 
      if (!children.forall(x => x.isModuleClass || x.isModule)) c.abort(
       c.enclosingPosition, 
       "All children must be objects." 
      ) else c.Expr[Set[A]] { 
       def sourceModuleRef(sym: Symbol) = Ident(
        if (sym.isModule) sym else 
         sym.asInstanceOf[ 
          scala.reflect.internal.Symbols#Symbol 
          ].sourceModule.asInstanceOf[Symbol] 
       ) 

       Apply(
        Select(
         reify(Set).tree, 
         newTermName("apply") 
        ), 
        children.map(sourceModuleRef(_)) 
       ) 
      } 
     } 
    } 
} 
1

看一看@ TravisBrown的question從無形的2.1.0-SNAPSHOT開始,他的問題中的代碼工作併產生一個枚舉ADT元素的Set,然後可以遍歷。我將在這裏回顧一下他的解決方案爲便於參考(fetchAllsort of mine :-))

import shapeless._ 

    trait AllSingletons[A, C <: Coproduct] { 
    def values: List[A] 
    } 

    object AllSingletons { 
    implicit def cnilSingletons[A]: AllSingletons[A, CNil] = 
     new AllSingletons[A, CNil] { 
     def values = Nil 
     } 

    implicit def coproductSingletons[A, H <: A, T <: Coproduct](implicit 
                   tsc: AllSingletons[A, T], 
                   witness: Witness.Aux[H] 
                   ): AllSingletons[A, H :+: T] = 
     new AllSingletons[A, H :+: T] { 
     def values: List[A] = witness.value :: tsc.values 
     } 
    } 

    trait EnumerableAdt[A] { 
    def values: Set[A] 
    } 

    object EnumerableAdt { 
    implicit def fromAllSingletons[A, C <: Coproduct](implicit 
                 gen: Generic.Aux[A, C], 
                 singletons: AllSingletons[A, C] 
                ): EnumerableAdt[A] = 
     new EnumerableAdt[A] { 
     def values: Set[A] = singletons.values.toSet 
     } 
    } 

    def fetchAll[T](implicit ev: EnumerableAdt[T]):Set[T] = ev.values