Итерация над запечатанной чертой в Скала?

Я просто хотел узнать, можно ли перебирать запечатанную черту в Скала? Если нет, то почему это невозможно? Так как черта запечатана, возможно, нет?

Я хочу сделать что-то вроде этого:

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, предоставив реализацию значениям enums. Есть ли эквивалент в Scala?

На самом деле это, на мой взгляд, подходящий вариант использования для макросов 2.10: вам нужен доступ к информации, которую вы знаете у компилятора, но не подвергаете экспорту, а macros дают вам (разумно) простой способ заглянуть внутрь. См. Мой ответ здесь для соответствующего (но теперь слегка устаревшего) примера или просто используйте что-то вроде этого:

 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) 

И все это совершенно безопасно – вы получите ошибку времени компиляции, если вы запрашиваете значения типа, который не запечатан, имеет неъектных детей и т. Д.

Вышеупомянутое решение, основанное на Scala Macros, отлично работает. Однако это не такие случаи, как:

 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(_)) ) } } } } 

Для этого нет возможности. Это не имело бы смысла в более распространенном случае, где вместо объектов корпуса у вас были фактические classы в качестве подclassа вашей запечатанной черты. Похоже, ваш случай может быть лучше обработан перечислением

 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 

См. Этот ответ в другом streamе . Библиотека Lloydmetas Enumeratum предоставляет java Enum подобные функции в легко доступном пакете с относительно небольшим шаблоном.

Взгляните на вопрос @ TravisBrown. Начиная с бесформенного 2.1.0-SNAPSHOT код, отправленный в его вопросе, работает и создает Set перечисленных элементов ADT, которые затем могут быть пройдены. Я приведу его решение здесь для удобства ссылок ( fetchAll – это своего рода :-))

 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 

Что-то, что может также решить проблему, – это возможность добавить неявное преобразование для добавления методов в перечисление, вместо того, чтобы повторять запечатанную черту.

 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) } 
Давайте будем гением компьютера.