2012-10-09 59 views
17

对不起,引人入胜的标题。 ;-)我可以在斯卡拉没有包私人类吗?

我想创建一个Scala中的一个包私有方法的包私有类,所以我的课看起来有点像这样:

package net.java.truevfs.ext.pace 

import ... 

private[pace] abstract class AspectController(controller: FsController) 
extends FsDecoratingController(controller) { 

    private[pace] def apply[V](operation: => V): V 

    ... // lots of other stuff 
} 

不过,如果我使用的javap检查什么Scala编译器有效地创建,我得到的是这样的:

$ javap -classpath target/classes net.java.truevfs.ext.pace.AspectController 
Compiled from "AspectController.scala" 
public abstract class net.java.truevfs.ext.pace.AspectController extends net.java.truevfs.kernel.spec.FsDecoratingController implements scala.ScalaObject{ 
    public abstract java.lang.Object apply(scala.Function0); 
    ... 
} 

这意味着,尽管Scala编译器会尊重准入限制,我仍然可以调用这个类从任何Java代码,这是一个明确的封装冲突。

我错过了什么吗? 有没有一种方法可以按预期做到这一点?

+2

+1的搞笑标题 –

回答

15

除了@雷吉斯的回答,原因Scala编译器不会使类包私人是因为斯卡拉规则它可以从其他包访问:即对net.java.truevfs.ext.pace子包。例如。

package net.java.truevfs.ext.pace.subpackage 
import net.java.truevfs.ext.pace.AspectController 

class Subclass extends AspectController { ... } 

是Scala合法的,但在Java从net.java.truevfs.ext.pace.subpackage类不能从net.java.truevfs.ext.pace访问包私有类。

+0

+1。其实我也会指出同样的观点。 –

+0

好点 - 我不知道这一点。 –

+0

嗯,这实际上意味着在Scala中根本不存在包私有类的概念。太糟糕了,因为对我来说,它一直是我的Java工具的基本元素,旨在促进封装(我明白,这种保护只能在语言层面上工作)。 –

9

你不会错过任何东西。 许多scala中的访问限制在java中都没有,也没有在jvm级别。附加信息显然就在.class文件中,但在那里只有scala编译器将解释的自定义注释。 scala对象模型只能部分与jvm对象模型匹配,而java编译器只能看到这个部分模型。 我想说这个匹配非常接近,而且scala编译器在java的互操作性方面做得非常好,但是没有什么是完美的。

+0

我是这么认为的,并承认在类中的@ scala.reflect.ScalaSignature注解。但我担心的是,这明显违反了封装,只要您可以从Java代码调用任何东西,就可以使用访问修饰符完全没有意义。 –

+3

那么,“无意义”的访问修饰符一般是毫无意义的,因为你总是可以通过反射/指针绕过它们。 – themel

+0

我不会说,你可以从java调用“任何东西”。如果你使用'apply' plain private(而不是私有的'pace'),那么你会发现这个方法确实在.class文件中被标记为private。这是因为scala和java中私有方法的概念完全相同,因此编译器可以将其标记为私有。另一方面,包的私有方法的概念在java/jvm中不存在。 –

1

不是一个真正的100%正确的答案...

你可以打个包对象,如果我想要做一些花哨的东西在里面有一个私有的类。包对象像任何其他包一样被访问。 类MyClass是该包对象的私有包。 然而它不是私人包装。

package object com.jasongoodwin.foo { 
    private class MyClass 

    class AnotherClass { 
    val myClass = new MyClass 
    } 
}