扩展Scala集合的简单示例 [英] Simple example of extending a Scala collection

查看:79
本文介绍了扩展Scala集合的简单示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一个非常简单的子类化Scala集合的例子。我对这一切是如何以及为何有效的全面解释并不感兴趣;有很多可用的此处其他地方在互联网上。我想知道这样做的简单方法。

I'm looking for a very simple example of subclassing a Scala collection. I'm not so much interested in full explanations of how and why it all works; plenty of those are available here and elsewhere on the Internet. I'd like to know the simple way to do it.

下面的类可能是一个尽可能简单的例子。这个想法是,创建一个 Set [Int] 的子类,它有一个额外的方法:

The class below might be as simple an example as possible. The idea is, make a subclass of Set[Int] which has one additional method:

class SlightlyCustomizedSet extends Set[Int] {
  def findOdd: Option[Int] = find(_ % 2 == 1)
}

显然这是错误的。一个问题是没有构造函数将东西放入 Set 。必须构建 CanBuildFrom 对象,最好通过调用一些已知的库代码来知道如何构建它。我已经看到在配对对象中实现几个附加方法的示例,但它们显示了它是如何工作的或如何做更复杂的事情。我想看看如何利用库中已有的东西来解决这几行代码问题。实现这个的最小,最简单的方法是什么?

Obviously this is wrong. One problem is that there's no constructor to put things into the Set. A CanBuildFrom object must be built, preferably by calling some already-existing library code that knows how to build it. I've seen examples that implement several additional methods in the companion object, but they're showing how it all works or how to do something more complicated. I'd like to see how to leverage what's already in the libraries to knock this out in a couple lines of code. What's the smallest, simplest way to implement this?

推荐答案

如果你只想向一个类添加一个方法,那么子类化可能不是要走的路。 Scala的集合库有点复杂,并且叶子类并不总是适合子类化(可以从子类化 HashSet 开始,但是这会让你开始深入了解兔子孔)。

If you just want to add a single method to a class, then subclassing may not be the way to go. Scala's collections library is somewhat complicated, and leaf classes aren't always amenable to subclassing (one might start by subclassing HashSet, but this would start you on a journey down a deep rabbit hole).

实现目标的一种更简单的方法可能是:

Perhaps a simpler way to achieve your goal would be something like:

implicit class SetPimper(val s: Set[Int]) extends AnyVal {
  def findOdd: Option[Int] = s.find(_ % 2 == 1)
}

这实际上并不是Set的子类,而是创建一个隐式转换,允许你做以下事情:

This doesn't actually subclass Set, but creates an implicit conversion that allows you to do things like:

Set(1,2,3).findOdd // Some(1)



打破兔子洞



如果你来自Java背景,那可能会令人惊讶扩展标准集合是如此困难 - 在所有Java标准库充满 juArrayList 子类之后,几乎任何可以包含其他内容的东西hings。但是,Scala有一个关键区别:它的首选集合都是不可变的。

Down the Rabbit Hole

If you've come from a Java background, it might be surprising that it's so difficult to extend standard collections - after all the Java standard library's peppered with j.u.ArrayList subclasses, for pretty much anything that can contain other things. However, Scala has one key difference: its first-choice collections are all immutable.

这意味着它们没有 add 就地修改它们的方法。相反,他们有 + 方法构建一个新实例,包含所有原始项目以及新项目。如果他们天真地实现这一点,那就非常低效,因此他们使用各种特定于类的技巧来允许新实例与原始实例共享数据。 + 方法甚至可以返回与原始对象不同的对象 - 一些集合类对小集合或空集合使用不同的表示。

This means that they don't have add methods that modify them in-place. Instead, they have + methods that construct a new instance, with all the original items, plus the new item. If they'd implemented this naïvely, it'd be very inefficient, so they use various class-specific tricks to allow the new instances to share data with the original one. The + method may even return an object of a different type to the original - some of the collections classes use a different representation for small or empty collections.

但是,这也意味着如果你想要继承其中一个不可变集合,那么你需要理解你正在子类化的类的内容,以确保你的子类的实例是以与基类相同的方式构造。

However, this also means that if you want to subclass one of the immutable collections, then you need to understand the guts of the class you're subclassing, to ensure that your instances of your subclass are constructed in the same way as the base class.

顺便说一句,如果你想要对可变集合进行子类化,这些都不适用于你。他们被视为scala世界中的二等公民,但他们添加方法,并且很少需要构建新实例。以下代码:

By the way, none of this applies to you if you want to subclass the mutable collections. They're seen as second class citizens in the scala world, but they do have add methods, and rarely need to construct new instances. The following code:

class ListOfUsers(users: Int*) extends scala.collection.mutable.HashSet[Int] {
  this ++= users

  def findOdd: Option[Int] = find(_ % 2 == 1)
}

在大多数情况下,你可能会做出或多或少的预期( map ,朋友可能不会做你所期待的,因为 CanBuildFrom 我会在一分钟内得到的东西,但请耐心等待。)

Will probably do more-or-less what you expect in most cases (map and friends might not do quite what you expect, because of the the CanBuildFrom stuff that I'll get to in a minute, but bear with me).

如果继承失败了,我们总是有一个核选项可以依靠:组成。我们可以创建自己的 Set 子类,将其职责委托给委托,如下所示:

If inheritance fails us, we always have a nuclear option to fall back on: composition. We can create our own Set subclass that delegates its responsibilities to a delegate, as such:

import scala.collection.SetLike
import scala.collection.mutable.Builder
import scala.collection.generic.CanBuildFrom

class UserSet(delegate: Set[Int]) extends Set[Int] with SetLike[Int, UserSet] {
    override def contains(key: Int) = delegate.contains(key)
    override def iterator = delegate.iterator
    override def +(elem: Int) = new UserSet(delegate + elem)
    override def -(elem: Int) = new UserSet(delegate - elem)
    override def empty = new UserSet(Set.empty)
    override def newBuilder = UserSet.newBuilder
    override def foreach[U](f: Int => U) = delegate.foreach(f) // Optional
    override def size = delegate.size // Optional
}

object UserSet {
    def apply(users: Int*) = (newBuilder ++= users).result()
    def newBuilder = new Builder[Int, UserSet] {
        private var delegateBuilder = Set.newBuilder[Int]
        override def +=(elem: Int) = {
            delegateBuilder += elem
            this
        }
        override def clear() = delegateBuilder.clear()
        override def result() = new UserSet(delegateBuilder.result())
    }

    implicit object UserSetCanBuildFrom extends CanBuildFrom[UserSet, Int, UserSet] {
        override def apply() = newBuilder
        override def apply(from: UserSet) = newBuilder
    }
}

这可以说太复杂,太简单了。它的代码行数比我们写的要多得多,然而,它仍然非常天真。

This is arguably both too complicated and too simple at the same time. It's far more lines of code than we meant to write, and yet, it's still pretty naïve.

没有伴侣类它会工作,但没有 CanBuildFrom map 将返回一个简单的 Set ,这可能不是你的期望。我们还重写了 Set 的文档建议我们实现的可选方法。

It'll work without the companion class, but without CanBuildFrom, map will return a plain Set, which may not be what you expect. We've also overridden the optional methods that the documentation for Set recommends we implement.

如果我们是彻底的,我们已经为我们的可变类创建了一个 CanBuildFrom ,并实现了 empty ,因为这样可以确保少数几个创建新实例的方法将按预期工作。

If we were being thorough, we'd have created a CanBuildFrom, and implemented empty for our mutable class, as this ensures that the handful of methods that create new instances will work as we expect.

如果这听起来太多了,请考虑以下内容:

If that sounds like too much work, consider something like the following:

case class UserSet(users: Set[Int])

当然,你必须输入几个字母来获取用户集,但是我认为它比子类更好地区分问题。

Sure, you have to type a few more letters to get at the set of users, but I think it separates concerns better than subclassing.

这篇关于扩展Scala集合的简单示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆