简体   繁体   English

为什么不推荐使用Scala软件包对象中的类?

[英]Why are classes inside Scala package objects dispreferred?

Starting with 2.10, -Xlint complains about classes defined inside of package objects. 从2.10开始, -Xlint抱怨软件包对象内部定义的类。 But why? 但为什么? Defining a class inside a package object should be exactly equivalent to defining the classes inside of a separate package with the same name, except a lot more convenient. 在包对象内定义类应该完全等同于在具有相同名称的单独包内定义类,只是更加方便。

In my opinion, one of the serious design flaws in Scala is the inability to put anything other than a class-like entity (eg variable declarations, function definitions) at top level of a file. 我认为,Scala中的严重设计缺陷之一是无法将除类之类的实体(例如,变量声明,函数定义)以外的任何东西放在文件的顶层。 Instead, you're forced to put them into a separate ''package object'' (often in package.scala ), separate from the rest of the code that they belong with and violating a basic programming rule which is that conceptually related code should be physically related as well. 相反,您被迫将它们放入单独的“包对象”(通常在package.scala ),与它们所属的其余代码分开,并违反了基本编程规则,即在概念上相关的代码应在身体上也是如此。 I don't see any reason why Scala can't conceptually allow anything at top level that it allows at lower levels, and anything non-class-like automatically gets placed into the package object, so that users never have to worry about it. 我没有看到任何理由说明Scala在概念上不允许在顶层允许任何在较低级别允许的东西,并且任何非类的东西都会自动放置到package对象中,因此用户不必担心。

For example, in my case I have a util package, and under it I have a number of subpackages ( util.io , util.text , util.time , util.os , util.math , util.distances , etc.) that group heterogeneous collections of functions, classes and sometimes variables that are semantically related. 例如,在我的情况下,我有一个util包,在它下面有许多子包( util.ioutil.textutil.timeutil.osutil.mathutil.distances等)将语义相关的功能,类甚至变量的异类集合进行分组。 I currently store all the various functions, classes, etc. in a package object sitting in a file called io.scala or text.scala or whatever, in the util directory. 我目前将所有各种函数,类等存储在util package object中的io.scalatext.scala或任何文件中的package object中。 This works great and it's very convenient because of the way functions and classes can be mixed, eg I can do something like: 这很好用,并且非常方便,因为可以混合使用函数和类,例如,我可以执行以下操作:

package object math {
  // Coordinates on a sphere

  case class SphereCoord(lat: Double, long: Double) { ... }

  // great-circle distance between two points
  def spheredist(a: SphereCoord, b: SphereCoord) = ...

  // Area of rectangle running along latitude/longitude lines
  def rectArea(topleft: SphereCoord, botright: SphereCoord) = ...

  // ...
  // ...

  // Exact-decimal functions
  class DecimalInexactError extends Exception

  // Format floating point value in decimal, error if can't do exactly
  formatDecimalExactly(val num: Double) = ...

  // ...
  // ...
}

Without this, I would have to split the code up inconveniently according to fun vs. class rather than by semantics. 没有这个,我将不得不根据乐趣与类而不是语义来不便地拆分代码。 The alternative, I suppose, is to put them in a normal object -- kind of defeating the purpose of having package objects in the first place. 我想,替代方法是将它们放置在普通对象中,这有点违背了将包装对象放在首位的目的。

But why? 但为什么? Defining a class inside a package object should be exactly equivalent to defining the classes inside of a separate package with the same name, 在包对象中定义类应该与在同一个名称相同的单独包中定义类完全相同,

Precisely. 精确地 The semantics are (currently) the same, so if you favor defining a class inside a package object, there should be a good reason. 语义(当前)是相同的,因此,如果您喜欢在包对象中定义类,则应该有充分的理由。 But the reality is that there is at least one good reason no to (keep reading). 但是现实是,至少有一个很好的理由拒绝(继续阅读)。

except a lot more convenient 除了更方便

How is that more convenient? 这样更方便吗? If you are doing this: 如果您这样做:

package object mypkg {
  class MyClass
}

You can just as well do the following: 您也可以执行以下操作:

package mypkg {
  class MyClass
}

You'll even save a few characters in the process :) 您甚至可以在此过程中保存几个字符:)

Now, a good and concrete reason not to go overboard with package objects is that while packages are open , package objects are not . 现在,一个不让包对象过度使用的良好而具体的理由是,当包打开时包对象没有 打开 A common scenario would be to have your code dispatched among several projects, with each project defining classes in the same package. 一种常见的情况是在多个项目之间分配代码,每个项目在同一包中定义类。 No problem here. 没问题 On the other hand, a package object is (like any object) closed (as the spec puts it "There can be only one package object per package"). 另一方面,包对象(与任何对象一样)是关闭的(如规范中所述:“每个包只能有一个包对象”)。 In other words, you will only be able to define a package object in one of your projects. 换句话说,您将只能在一个项目中定义一个包对象。 If you attempt to define a package object for the same package in two distinct projects, bad things will happen, as you will effectively end up with two distinct versions of the same JVM class (n our case you would end up with two "mypkg.class" files). 如果您尝试在两个不同的项目中为同一软件包定义一个软件包对象,则会发生不好的事情,因为您将有效地得到同一JVM类的两个不同版本(在我们的情况下,您将得到两个“ mypkg”。类”文件)。 Depending on the cases you might end up with the compiler complaining that it cannot find something that you defined in the first version of your package object, or get a "bad symbolic reference" error, or potentially even a runtime error. 根据情况的不同,您可能最终以编译器抱怨找不到它在包对象的第一个版本中定义的内容,或者得到“错误的符号引用”错误,甚至可能是运行时错误。 This is a general limitation of package objects, so you have to be aware of it. 这是包对象的一般限制,因此您必须意识到这一点。 In the case of defining classes inside a package object, the solution is simple: don't do it (given that you won't gain anything substantial compared to just defining the class as a top level). 在包对象内部定义类的情况下,解决方案很简单:不要这样做(因为与仅将类定义为顶级相比,您不会获得任何实质性的收益)。 For type aliase, vals and vars, we don't have such a luxuary, so in this case it is a matter of weighing whether the syntactic convenience (compared to defining them in an object) is worth it, and then take care not to define duplicate package objects. 对于类型别名,vals和vars,我们没有这样的奢侈,因此在这种情况下,要权衡语法便利性(与在对象中定义它们相比)是否值得,然后注意不要定义重复的包对象。

I have not found a good answer to why this semantically equivalent operation would generate a lint warning. 对于这个语义上等效的操作为什么会产生皮棉警告,我还没有找到好的答案。 Methinks this is a lint bug. 我认为这是一个皮棉错误。 The only thing that I have found that must not be placed inside a package object (vs inside a plain package) is an object that implements main (or extends App ). 我发现唯一不能放置在包对象内(相对于普通包内)的是实现main (或extends App )的对象。

Note that -Xlint also complains about implicit classes declared inside package objects, even though they cannot be declared at package scope. 请注意,即使无法在包范围内声明隐式类,-Xlint也会抱怨在包对象内部声明的隐式类。 (See http://docs.scala-lang.org/overviews/core/implicit-classes.html for the rules on implicit classes.) (有关隐式类的规则,请参见http://docs.scala-lang.org/overviews/core/implicit-classes.html 。)

I figured out a trick that allows for all the benefits of package objects without the complaints about deprecation. 我想出了一个技巧,可以使包对象获得所有好处,而不必抱怨过时。 In place of 代替

package object foo {
  ...
}

you can do 你可以做

protected class FooPackage {
  ...
}

package object foo extends FooPackage { }

Works the same but no complaint. 工作原理相同,但没有任何抱怨。 Clear sign that the complaint itself is bogus. 明确的迹象表明投诉本身是伪造的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM