【问题标题】:Implicitly add to collection隐式添加到集合
【发布时间】:2019-01-04 12:26:31
【问题描述】:

我有一个 Set,我想在调用该 Set 时将另一个值隐式添加到该 Set 的末尾。

例如,我需要这样的东西才能工作:

implicit def addToSet(set: Set[Int]) = set + 4
val s = Set(1, 2, 3)
println(s) // Set(1, 2, 3, 4)

用例:

我正在构建一个网站(使用 PlayFramework),我有大约 20-30 组角色,它们对应于不同的功能。对于每个页面,只有某些角色可以访问某些功能,所以每次我需要检查角色权限时我都有一些功能(伪代码:if(role.canAccess(/*name of page*/))),它检查用户登录的角色是否包含在/*name of page*/对应的Set。

我现在正在创建一个超级用户,它可以访问每个页面上的每个功能,因此用户的“角色”需要在每个集合中。我不想通过手动将此角色添加到每个 Set 来执行此操作,因为我希望它具有可扩展性且易于维护,因此我希望有一个解决方案,它只是在幕后自动将这个新的超级角色添加到每个 Set .

我也愿意接受非隐式答案,只要他们不修改我已有的原始集合。

【问题讨论】:

  • 隐式 def 会创建一个隐式转换。所以,如果你需要一个 Set[Long] 并且你提供一个 Set[Int] 它会选择它......没有理由编译器去转换那个集合,所以在那里什么都不会做。只需创建一个与您的集合一起使用的小方法,如果您使用的是不可变集合,则更改它没有问题,您将返回一个新集合。此外,隐式转换真的很诱人,因为代码“干净”,但意味着你隐藏了代码的真正作用。
  • @LuisMiguelMejíaSuárez 现在我已经做了类似于拉曼建议的事情——在这个例子中,它类似于object MyObj { implicit class MyClass(in: Set[Int]) { def values = in + 4 } }; import MyObj._; ...——我在我的每个集合之后都放了.values。我宁愿不碰我原来的套装,但这是我目前想出的最干净的解决方案。
  • @JamesWhiteley 带有扩展方法的隐式类是个好主意!因为它们很清晰,而且隐含性很明确(听起来很奇怪,但我希望你明白我的意思)。如果您想要更多的类型安全,您可以为角色创建自己的类型(它可能是一个简单的类型别名),并仅为角色集创建隐式类,并且该方法可以称为@ 987654327@ 这将使一切变得超级清晰。我知道在每个地方都这样做很痛苦,但总比你无法控制的黑魔法好。
  • @JamesWhiteley 另外,如果您打算使用隐式类解决方案,您可以查看value classes,以避免实例化每个隐式类的成本。
  • 你为什么不抽象 canAccess(role, allowed) 方法或控制器的共同特征或类似的东西。这个方法可以实现为role == SuperAdmin || allowed.contains(role)

标签: scala implicit


【解决方案1】:

我想这就是你要找的东西:

trait Implicits {
  implicit class AddIntoSet[T](set: Set[T]) {
    def add(t: T): Set[T] = {
      set.+(t)
    }
  }
}


object Solution1 extends App with Implicits {
  val s = Set(1, 2, 3)
  println(s.add(4)) // Set(1, 2, 3, 4)
}

【讨论】:

  • 感谢您的回答,但我希望不必手动调用方法即可应用该方法。即,我希望能够编写 println(s) 并应用该方法,而无需编写 .add(4)
【解决方案2】:

正如其他人所说,这可能不是一个好主意,但如果你真的必须这样做,你可以尝试滚动你自己的 Set 对象并稍作修改:

import scala.collection.generic.SetFactory
import scala.collection.{GenSet, Set, immutable}

object CustomSet extends SetFactory[Set]{
  def newBuilder[A] = immutable.Set.newBuilder[A]

  def apply[T](elems: T*)(implicit extra: T): Set[T] = {
    val orig: Set[T] = super.apply(elems).flatten
    val withExtra: Set[T] = orig union GenSet(extra)
    withExtra
  }
}

implicit val extraInt: Int = 4
implicit val extraStr: String = "c"

val myNumNewSet: Set[Int] = CustomSet(1,2,3)
val myStrNewSet: Set[String] = CustomSet("a", "b")

这些将返回:

scala> val myNumNewSet: Set[Int] = CustomSet(1,2,3)
myNumNewSet: scala.collection.Set[Int] = Set(1, 2, 3, 4)

scala> val myStrNewSet: Set[String] = CustomSet("a", "b")
myStrNewSet: scala.collection.Set[String] = Set(a, b, c)

【讨论】:

    【解决方案3】:

    也许你可以接受使用类型别名?

    object Roles {
      type Role = Int //Optionally you can use type tagging to make it more type-safe
      type Roles = Set[Role]
    
      val SuperRole = 4
    
      def apply(roles: Role*): Roles = {
        Set(SuperRole) ++ roles
      }
    }
    
    
    
    println(Roles(1,2,3)) // Set(4, 1, 2, 3)
    println(Roles() ++ Set(5,6)) // you can also use all methods from Set
    

    这还有一个好处,就是如果您在角色以外的任何其他范围内使用它,它不会干扰:

    val idsOfUsers = Set(10,12,22) // Set(10, 12, 22)
    val roles = Roles(10,12,22) //Set(4, 10, 12, 22)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-08
      • 1970-01-01
      • 2012-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多