3
votes

Ajouter implicitement à la collection

J'ai un ensemble, et je veux ajouter implicitement une autre valeur à la fin de cet ensemble lorsqu'il est appelé.

Par exemple, j'ai besoin de quelque chose comme ça pour fonctionner:

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


8 commentaires

La définition implicite créerait une conversion implicite .. donc, si vous aviez besoin d'un Set [Long] et que vous fournissiez un Set [Int], il le ramasserait ... Il n'y a aucune raison pour que le compilateur aille convertir cet ensemble, donc rien ne sera fait là-dedans. Créez simplement une petite méthode à utiliser avec votre ensemble, si vous utilisez un ensemble immuable, vous n'avez aucun problème à changer cela, et vous retournerez un NOUVEAU ensemble. En outre, les conversions implicites sont vraiment tentantes, car elles provoquent un code «propre», mais cela signifie que vous cachez ce que fait réellement le code.


@FerranJr mes ensembles actuels sont immuables; Je suis d'accord avec le retour d'un nouvel ensemble. Je préfère ne pas appeler manuellement une petite méthode chaque fois que chacun de ces ensembles est référencé, car ils sont référencés littéralement des centaines de fois dans mon application. De préférence, j'aimerais que quelque chose se passe dans les coulisses sans que je doive le référencer, sinon cela ne vaudrait pas la peine de le faire plutôt que d'ajouter mon nouveau rôle à chaque ensemble manuellement.


@JamesWhiteley, à mon humble avis, ce serait une mauvaise idée que ce genre de choses puisse être fait, au début il semble que le code soit plus "propre" , mais comme Ferran l'a déjà dit, cela devient vraiment obscur, et cela peut devenir incontrôlable et causer des bogues - par exemple, jetez un œil à ceci . Je pense que vous pouvez rechercher une approche différente.


@ LuisMiguelMejíaSuárez pour l'instant j'ai fait quelque chose de similaire à la suggestion de Raman - avec cet exemple, ce serait quelque chose comme object MyObj {classe implicite MyClass (in: Set [Int]) {def values ​​= in + 4}}; import MyObj._; ... - et j'ai mis .values ​​ après chacun de mes ensembles. J'aurais préféré ne pas toucher à mes sets d'origine, mais c'est la solution la plus propre que j'ai proposée pour le moment.


@ LuisMiguelMejíaSuárez parce que ce serait essentiellement un morceau de code privé et serait assez petit et local pour un fichier, je ne pense pas que ce serait un problème dans mon scénario réel. Je comprends cependant pourquoi il y a des hésitations sur ce que j'essaie de faire à plus grande échelle, et c'était certainement une lecture intéressante.


Les classes implicites @JamesWhiteley avec des méthodes d'extension sont une excellente idée !, car elles sont claires et explicites dans la mesure où elles impliquent (cela semble bizarre, mais j'espère que vous comprenez ce que je veux dire) . Si vous voulez encore plus de sécurité de type, vous pouvez créer votre propre type pour les rôles (il peut s'agir d'un simple alias de type) et créer la classe implicite uniquement pour les ensembles de rôles, et la méthode pourrait être appelée quelque chose comme < code> thisPlusSuperRole qui rendra tout super clair. Je sais que c'est une douleur dans le cul de faire ça partout, mais c'est mieux que la magie noire que vous ne contrôlez pas.


@JamesWhiteley De plus, si vous prévoyez d'utiliser la solution de classe implicite, vous pouvez jeter un œil à classes de valeur , pour éviter le coût d'instanciation de chaque classe implicite.


Pourquoi ne pas simplement abstraire la méthode canAccess (role, allowed) ou quelque chose de similaire dans le trait commun pour les contrôleurs ou quelque chose comme ça. Cette méthode pourrait être implémentée en tant que role == SuperAdmin || allowed.contains (rôle)


3 Réponses :


2
votes

Je pense que c'est ce que vous recherchez:

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


1 commentaires

Merci pour votre réponse, mais je cherche à ne pas avoir à appeler une méthode manuellement pour que cette méthode soit appliquée. Ie, je voudrais juste pouvoir écrire println (s) et avoir la méthode appliquée sans que j'aie à écrire le .add (4) .



0
votes

Peut-être que l'utilisation d'alias de type serait acceptable pour vous?

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

Cela présente également un avantage, car si vous l'utilisez dans une autre portée que les rôles, cela n'interférerait pas:

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


0 commentaires

1
votes

Comme d'autres l'ont dit, ce n'est peut-être pas une très bonne idée, mais si vous le devez vraiment, vous pouvez essayer de lancer votre propre objet Set avec de légères modifications:

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)

Et ceux-ci renverront:

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")


0 commentaires