【问题标题】:Why people define class, trait, object inside another object in Scala?为什么人们在 Scala 的另一个对象中定义类、特征、对象?
【发布时间】:2011-01-13 14:47:15
【问题描述】:

好的,我会解释我为什么问这个问题。这些天我开始阅读 Lift 2.2 的源代码。 如果你之前碰巧读过lift源代码就好了。

在Lift中,我发现,定义内部类和内部特征被大量使用。

object Menu 有 2 个内部特征和 4 个内部类。 Loc 对象有 18 个内部类,5 个内部特征,7 个内部对象。

有很多这样的代码。我想知道作者为什么会这样写。

  • 是不是因为是作者的 个人品味或强大的使用 语言功能?
  • 这种类型有什么取舍吗 用途?

【问题讨论】:

    标签: scala lift


    【解决方案1】:

    在 2.8 之前,您必须在包和对象之间进行选择。包的问题在于它们不能单独包含方法或 val。因此,您必须将所有这些都放在另一个对象中,这会变得很尴尬。观察:

    object Encrypt {
      private val magicConstant = 0x12345678
      def encryptInt(i: Int) = i ^ magicConstant
      class EncryptIterator(ii: Iterator[Int]) extends Iterator[Int] {
        def hasNext = ii.hasNext
        def next = encryptInt(ii.next)
      }
    }
    

    现在您可以通过import Encrypt._ 访问方法encryptInt 以及类EncryptIterator。好用!

    相比之下,

    package encrypt {
      object Encrypt {
        private[encrypt] val magicConstant = 0x12345678
        def encryptInt(i: Int) = i ^ magicConstant
      }
      class EncryptIterator(ii: Iterator[Int]) extends Iterator[Int] {
        def hasNext = ii.hasNext
        def next = Encrypt.encryptInt(ii.next)
      }
    }
    

    这不是一个巨大的区别,但它会让用户同时导入encrypt._encrypt.Encrypt._,或者不得不一遍又一遍地写Encrypt.encryptInt。为什么不像第一个模式那样只使用一个对象呢? (实际上没有性能损失,因为嵌套类实际上并不是 Java 内部类;就 JVM 所知,它们只是常规类,但具有告诉您它们是嵌套的花哨名称。)

    在 2.8 中,您也可以吃蛋糕:将事物称为包对象,编译器将为您重写代码,因此它实际上看起来像第二个示例(对象 Encrypt 除外)实际上在内部称为package),但在命名空间方面的行为与第一个示例相似——vals 和defs 就在那里,不需要额外的导入。

    因此,在 2.8 之前开始的项目通常使用对象来封装很多东西,就好像它们是一个包一样。在 2.8 之后,主要动机之一已被删除。 (但要明确一点,使用对象仍然没有坏处;更多的是它在概念上具有误导性,而不是它对性能或诸如此类的负面影响。)

    (请注意,请不要尝试以这种方式实际加密任何内容,除非作为示例或玩笑!)

    【讨论】:

    • 对象中的嵌套类和Java的静态内部类完全一样吗?如果我将 100 个内部类或 100 个内部对象放在一个伴生对象中,编译器将如何处理它们?绝对没有性能损失?
    • 类或对象 Foo 内的嵌套类 Bar 不是内部类,而是名称为 Foo$Bar外部 类,并且仅当它实际上需要使用它的父类,它是否有一个(某种隐藏的)指向父类的字段。编译器而不是 JVM 会强制您正确使用它,对于一个对象,它知道只有一个,因此它不需要费心存储该字段。
    【解决方案2】:

    当您想使用抽象类型变量时,有时需要将类、特征和对象放入对象中,例如http://programming-scala.labs.oreilly.com/ch12.html#_parameterized_types_vs_abstract_types

    【讨论】:

      【解决方案3】:

      两者都可以。除其他外,内部类/特征的实例可以访问其父级的变量。必须使用父实例创建内部类,该父实例是外部类型的实例。

      在其他情况下,它可能只是将密切相关的事物分组的一种方式,例如您的 object 示例。请注意,特征 LocParam 是密封的,这意味着所有子类都必须在同一个编译单元/文件中。

      【讨论】:

      • 使用对象来分组相关的东西我感觉不太好,我认为是包的用法。
      • @Zwcat:你说的很有道理。然而,为了方便起见,将所有这些小东西放在一个文件中而不是在同一个目录中的这么多文件中可能会更轻松。使用sealed关键字使得有必要将它们放在同一个文件中并将它们包装在一个包含对象中看起来不那么混乱。
      【解决方案4】:

      sblundy 有一个不错的答案。要添加的一件事是,只有 Scala 2.8 才有包对象,它允许您在包命名空间中将类似的东西分组,而无需创建一个完全独立的对象。出于这个原因,我将更新我的 Lift Modules 提案以使用包对象而不是简单对象。

      【讨论】:

        猜你喜欢
        • 2017-10-25
        • 2015-05-26
        • 1970-01-01
        • 1970-01-01
        • 2016-07-02
        • 2013-10-30
        • 1970-01-01
        • 2012-06-08
        • 1970-01-01
        相关资源
        最近更新 更多