在Java中,通过将源文件放在像package com.acme.foo
这样的子目录中,将类放在包含com/acme/foo
和之类声明的包中。
我正在开发一种JVM语言,它的目的是为了更多不要重复自己的样式而不是Java,所以我将使用这些机制中的一种或另一种,但不是两种,我想知道要使用哪个。
Scala和Clojure等其他JVM语言如何处理它?他们需要两种机制还是只需一种机制,如果是这样的话?
答案 0 :(得分:11)
正如问题Travis Brown的评论中所提到的那样,scalac没有对源文件的路径或名称设置任何此类约束或限制,如果您在一个文件中甚至可以指定多个包愿望。
但是,scalac生成的字节码会将字节码类文件放入相应类加载器所需的必要目录结构中。
以下是一些展示灵活性的例子(我不一定提倡这些风格,只是展示了灵活性)。
// in packages1.scala file in local directory
package my {
package states {
sealed trait State
case class CheckingOut(shoppingCartId: Long) extends State
case class ConfirmedOrder(orderId: Long) extends State
case class ItemShipped(orderId: Long, itemId: Long, quantity: Int) extends State
}
}
这就是......
// in packages2.scala file
package com.foo.bar
sealed trait Scale
case object WebScale extends Scale
case object LolScale extends Scale
case object RoflScale extends Scale
case object WatScale extends Scale
这种风格也是可能的:
// in packages3.scala file
package foo {
package awesomeness {
class Main extends App {
println("Bananas are awesome")
}
}
}
package foo {
package lameness {
class Main extends App {
println("Congress is pretty lame, honestly")
}
}
}
以及......
package foo
package bar
// now we are in package foo.bar for remainder of file unless another package statement is made
以下是生成的源代码和编译的字节码树:
$ tree
.
├── com
│ └── foo
│ └── bar
│ ├── LolScale$.class
│ ├── LolScale.class
│ ├── RoflScale$.class
│ ├── RoflScale.class
│ ├── Scale.class
│ ├── WatScale$.class
│ ├── WatScale.class
│ ├── WebScale$.class
│ └── WebScale.class
├── foo
│ ├── awesomeness
│ │ ├── Main$delayedInit$body.class
│ │ └── Main.class
│ └── lameness
│ ├── Main$delayedInit$body.class
│ └── Main.class
├── my
│ └── states
│ ├── CheckingOut$.class
│ ├── CheckingOut.class
│ └── State.class
├── packages1.scala
├── packages2.scala
└── packages3.scala
8 directories, 19 files
我不确定Clojure是否支持这种灵活性,但Clojure约定是使用Java常规来构造源代码,使用它常用的构建工具lein
(参见leiningen tutorial here)。
然而,需要注意的一点是,在Scala和Clojure中,似乎与Java世界中经常使用的$DOMAIN.$APPLICATION
格式不同(例如com.oracle.jdbc...
,{{ 1}}等。在Scala中,您会看到包名称的$ DOMAIN部分被完全取出(例如org.hibernate.session...
,scalaz...
等。)
另外值得注意的是您可以从Scala中导出包的方式:
akka.{actor,io, ...}
包中的所有公开“内容”:foo.bar
import foo.bar._
import foo.bar.Baz
import foo.bar.{Baz => FooBarBaz}
另外值得注意的是Scala中的包私有作用域:
import foo.bar.{Baz, Boo, Bla}
以上package foo.bar
class Baz{
private[bar] def boo[A](a: A)
private[foo] def bla[A](a: A)
}
是boo
包(和子包)的私有,而foo.bar
是bla
及其所有子包的私有。
有关详细信息,请阅读Scala语言规范和相关链接:
答案 1 :(得分:1)
Clojure有点像Java,但是没有真正的类,所以它有点不同。
如果有一个名称空间project.foo,那么Clojure编译器会在项目目录下的一个名为foo.clj的文件的源根目录下找到它。
e.g。假设src是保存源文件的目录。然后project.foo命名空间将在src/project/foo.clj
中,并且顶部会有(ns project.foo)
之类的声明。
我认为很难摆脱这种情况,因为在运行时,当Clojure加载文件时(大多数Clojure被捆绑为源代码),Clojure运行时会将文件作为资源加载,这要求它是在目录层次结构中的正确位置(无论是基于jar还是基于文件)。
就个人而言,我并不介意包名==目录位置约定。它使工具可以轻松地在编译或运行时查找文件,如果我只是使用emacs,也可以让我轻松找到它们。它还感觉有点组织,然后只是一个目录中的一堆文件,虽然这可能更多的是我一直在使用它之后的习惯。
答案 2 :(得分:0)
我对Clojure没有任何经验,但我确实有一些使用Scala的经验,scala也使用子目录作为他们的包,但其中的一个很好的功能是重命名类的能力类似于C ++ typedef关键字功能。一个有趣的想法是,如果你创建了一个类似于C ++的链接器并允许一个typedef功能但仍然保留在JVM中。