如何为 Scala 中的任何集合创建泛型隐式 class?
How to make a generic implicit class for any collection in Scala?
我想写一个方法,returns None
如果集合为空,Some(collection)
在其他情况下。
我能得到的最好的是
implicit class CollectionExtensions[A, Repr](self: TraversableLike[A, Repr]){
def toOption: Option[Repr] = if (self.nonEmpty) Some(self.asInstanceOf[Repr]) else None
}
但是转换 .asInstanceOf[Repr]
似乎是错误的。正确的做法是什么?
为了恢复原来的Repr
类型,可以使用self.repr
(scaladoc)
implicit class CollectionExtensions[A, Repr](self: TraversableLike[A, Repr]){
def toOption: Option[Repr] = if (self.nonEmpty) Some(self.repr) else None
}
如果您像@chengpohi 的回答所暗示的那样坚持使用 Option[TraversableLike[A, Repr]]
,那么对它 (list.toOption.map(_.map(x => x))
) 进行 map
之类的操作将 return 您 Option[Traversable[T]]
失去原来的 Repr
类型(如 List[Int]
)。 repr
对此有帮助:
def repr: Repr
The collection of type traversable collection underlying this
TraversableLike object. By default this is implemented as the
TraversableLike object itself, but this can be overridden.
然而,有趣的是,如果您查看 repr
的代码 (here):
def repr: Repr = this.asInstanceOf[Repr]
它做同样的事情,但至少它很好地包装(隐藏?)在 scala-library 中并且更抽象,所以通过使用它你可以解释潜在的重新定义。
此外,值得一提的是,这种非空集合方法在scalaz/cats中很流行:
scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._
scala> List(1, 2, 3).toNel
res8: Option[scalaz.NonEmptyList[Int]] = Some(NonEmptyList(1, 2, 3))
scala> nil[Int].toNel
res9: Option[scalaz.NonEmptyList[Int]] = None
toNel
这里的意思是toNonEmptyList
,所以它不像你的解决方案那么抽象。猫有 OneAnd[A,Repr]
和一些有用的暗示。参见 http://typelevel.org/cats/datatypes/oneand.html
除了 @dk14 回答之外,您还可以使用其他一些方法:
对 Repr with TraversableOnce[A]
使用隐式 class。这也将支持 Iterator
,因为 Iterator
扩展 TraversableOnce
,但不扩展 TraversableLike
。
implicit class CollectionExtensions[A, Repr](val self: Repr with TraversableOnce[A]) extends AnyVal {
def toOption: Option[Repr] = if (self.nonEmpty) Some(self) else None
}
仅对 Repr
使用隐式 class,但要求证明它可以隐式转换为 Traversable
。这种方法还支持 Array
s 和 String
s,因为它们根本不扩展 Traversable
,但可以隐式转换为它。
implicit class CollectionExtensions[Repr](val self: Repr) extends AnyVal {
def toOption[A](implicit ev: Repr => TraversableOnce[A]): Option[Repr] = {
val traversable = ev(self)
if (traversable.isEmpty) None else Some(self)
}
}
这两种方法都保留了原始类型:
scala> List(1, 2, 3).toOption
res1: Option[List[Int]] = Some(List(1, 2, 3))
scala> Iterator(1, 2, 3).toOption
res2: Option[Iterator[Int]] = Some(non-empty iterator)
scala> Array.empty[Int].toOption
res3: Option[Array[Int]] = None
scala> Map(1 -> 2).toOption
res4: Option[scala.collection.immutable.Map[Int,Int]] = Some(Map(1 -> 2))
scala> "123".toOption
res5: Option[String] = Some(123)
scala> "".toOption
res6: Option[String] = None
我想写一个方法,returns None
如果集合为空,Some(collection)
在其他情况下。
我能得到的最好的是
implicit class CollectionExtensions[A, Repr](self: TraversableLike[A, Repr]){
def toOption: Option[Repr] = if (self.nonEmpty) Some(self.asInstanceOf[Repr]) else None
}
但是转换 .asInstanceOf[Repr]
似乎是错误的。正确的做法是什么?
为了恢复原来的Repr
类型,可以使用self.repr
(scaladoc)
implicit class CollectionExtensions[A, Repr](self: TraversableLike[A, Repr]){
def toOption: Option[Repr] = if (self.nonEmpty) Some(self.repr) else None
}
如果您像@chengpohi 的回答所暗示的那样坚持使用 Option[TraversableLike[A, Repr]]
,那么对它 (list.toOption.map(_.map(x => x))
) 进行 map
之类的操作将 return 您 Option[Traversable[T]]
失去原来的 Repr
类型(如 List[Int]
)。 repr
对此有帮助:
def repr: Repr
The collection of type traversable collection underlying this TraversableLike object. By default this is implemented as the TraversableLike object itself, but this can be overridden.
然而,有趣的是,如果您查看 repr
的代码 (here):
def repr: Repr = this.asInstanceOf[Repr]
它做同样的事情,但至少它很好地包装(隐藏?)在 scala-library 中并且更抽象,所以通过使用它你可以解释潜在的重新定义。
此外,值得一提的是,这种非空集合方法在scalaz/cats中很流行:
scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._
scala> List(1, 2, 3).toNel
res8: Option[scalaz.NonEmptyList[Int]] = Some(NonEmptyList(1, 2, 3))
scala> nil[Int].toNel
res9: Option[scalaz.NonEmptyList[Int]] = None
toNel
这里的意思是toNonEmptyList
,所以它不像你的解决方案那么抽象。猫有 OneAnd[A,Repr]
和一些有用的暗示。参见 http://typelevel.org/cats/datatypes/oneand.html
除了 @dk14 回答之外,您还可以使用其他一些方法:
对
Repr with TraversableOnce[A]
使用隐式 class。这也将支持Iterator
,因为Iterator
扩展TraversableOnce
,但不扩展TraversableLike
。implicit class CollectionExtensions[A, Repr](val self: Repr with TraversableOnce[A]) extends AnyVal { def toOption: Option[Repr] = if (self.nonEmpty) Some(self) else None }
仅对
Repr
使用隐式 class,但要求证明它可以隐式转换为Traversable
。这种方法还支持Array
s 和String
s,因为它们根本不扩展Traversable
,但可以隐式转换为它。implicit class CollectionExtensions[Repr](val self: Repr) extends AnyVal { def toOption[A](implicit ev: Repr => TraversableOnce[A]): Option[Repr] = { val traversable = ev(self) if (traversable.isEmpty) None else Some(self) } }
这两种方法都保留了原始类型:
scala> List(1, 2, 3).toOption
res1: Option[List[Int]] = Some(List(1, 2, 3))
scala> Iterator(1, 2, 3).toOption
res2: Option[Iterator[Int]] = Some(non-empty iterator)
scala> Array.empty[Int].toOption
res3: Option[Array[Int]] = None
scala> Map(1 -> 2).toOption
res4: Option[scala.collection.immutable.Map[Int,Int]] = Some(Map(1 -> 2))
scala> "123".toOption
res5: Option[String] = Some(123)
scala> "".toOption
res6: Option[String] = None