Подтвердить что ты не робот

Почему классы внутри объектов пакета Scala выбраны?

Начиная с 2.10, -Xlint жалуется на классы, определенные внутри объектов пакета. Но почему? Определение класса внутри объекта пакета должно быть в точности эквивалентным определению классов внутри отдельного пакета с тем же именем, кроме гораздо более удобного.

По моему мнению, одним из серьезных недостатков дизайна в Scala является невозможность помещать на верхнем уровне файла ничего, кроме класса (например, объявления переменных, определения функций). Вместо этого вы вынуждены помещать их в отдельный "объект пакета" (часто в package.scala), отдельно от остальной части кода, к которому они принадлежат, и нарушая основное правило программирования, которое является тем, что концептуально связанный код должен быть физически связанными также. Я не вижу причин, почему Scala не может концептуально разрешить что-либо на верхнем уровне, которое он допускает на более низких уровнях, и что-то неклассическое автоматически помещается в объект пакета, так что пользователям никогда не придется беспокоиться о он.

Например, в моем случае у меня есть пакет util, и под ним у меня есть несколько подпакетов (util.io, util.text, util.time, util.os, util.math, util.distances и т.д.), которые группируют гетерогенные коллекции функций, классов и иногда переменных, которые семантически связаны между собой. В настоящее время я храню все различные функции, классы и т.д. В package object, сидящем в файле с именем io.scala или text.scala или любым другим, в каталоге util. Это отлично работает, и это очень удобно из-за того, что функции и классы могут быть смешаны, например. Я могу сделать что-то вроде:

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) = ...

  // ...
  // ...
}

Без этого мне пришлось бы разбить код неудобно в соответствии с потехой и классом, а не с семантикой. Альтернатива, я полагаю, заключается в том, чтобы поместить их в нормальный объект - вид победить цель иметь объекты пакета в первую очередь.

4b9b3361

Ответ 1

Но почему? Определение класса внутри объекта пакета должно быть в точности эквивалентным определению классов внутри отдельного пакета с тем же именем,

Точно. Семантика (в настоящее время) одинакова, поэтому, если вы предпочитаете определять класс внутри объекта пакета, должна быть веская причина. Но реальность такова, что есть по крайней мере одна хорошая причина: нет (продолжать читать).

кроме гораздо более удобного

Как это удобнее? Если вы это делаете:

package object mypkg {
  class MyClass
}

Вы также можете сделать следующее:

package mypkg {
  class MyClass
}

Вы даже сохраните несколько символов в процессе:)

Теперь хорошая и конкретная причина не переходить за пределы с объектами пакета заключается в том, что пока пакеты открыты, объекты пакета не. Обычный сценарий будет заключаться в том, чтобы ваш код отправлялся между несколькими проектами, причем каждый проект определял классы в одном пакете. Здесь нет проблем. С другой стороны, объект пакета (как любой объект) закрыт (по мере того как спецификация помещает его "Может быть только один объект пакета на пакет" ). Другими словами, вы сможете определить только объект пакета в одном ваших проектов. Если вы попытаетесь определить объект пакета для одного и того же пакета в двух разных проектах, будут происходить плохие вещи, так как вы фактически получите два отдельные версии одного и того же класса JVM (в нашем случае вы получите два файла "mypkg.class" ). В зависимости от случаев, когда вы можете столкнуться с компилятором, жалующимся на то, что он не может найти то, что вы определили в первой версии вашего объекта пакета, или получить ошибку "плохой символической ссылки" или потенциально даже ошибку времени выполнения. Это общее ограничение объектов пакета, поэтому вы должны знать об этом. В случае определения классов внутри объекта пакета решение прост: не делайте этого (учитывая, что вы не получите ничего существенного по сравнению с просто определением класса как верхнего уровня). Для типа aliase, vals и vars у нас нет такого роскошного, так что в этом случае важно оценить, насколько удобно синтаксическое удобство (по сравнению с определением их в объекте), а затем позаботиться о том, чтобы определить дублирующие объекты пакета.

Ответ 2

Я не нашел хорошего ответа на вопрос, почему эта семантически эквивалентная операция будет генерировать предупреждение lint. Понятно, что это ошибка ворса. Единственное, что я обнаружил, что не должен размещаться внутри объекта пакета (vs внутри простого пакета) - это объект, который реализует main (или extends App).

Обратите внимание, что -Xlint также жалуется на неявные классы, объявленные внутри объектов пакета, даже если они не могут быть объявлены в области пакета. (См. http://docs.scala-lang.org/overviews/core/implicit-classes.html для правил о неявных классах.)

Ответ 3

Я выяснил трюк, который позволяет использовать все преимущества объектов пакета без жалоб на усталость. Вместо

package object foo {
  ...
}

вы можете сделать

protected class FooPackage {
  ...
}

package object foo extends FooPackage { }

Работает так же, но без жалобы. Ясный признак того, что сама жалоба является фиктивной.