如何在Java中在两个包之间共享包私有数据?

6
我有两个Java包:A和B。假设B中的一些类想要使用A中的一些类,然而,当开发人员开始开发C包(或C应用程序),他/她将使用我的B包,但我不希望他/她能够使用B正在使用的A类。也就是说,我希望A包中的类是包私有的,以便隐藏它们不被应用程序开发人员使用。然而,我确实希望我的B包能够访问那些包私有的类。在Java中是否可以做到这一点?我基本上只需要咬紧牙关将这些类设置为公共类,但希望用户不会尝试使用它们?还是说我需要在B内部复制A中的类? 我的首选是一种不hack-y的方法(即,我不想使用反射)。帮忙吗?

如果包B可以使用包A中的类做某些事情,那么没有什么可以阻止包C使用包A中的类做同样的事情。然而,除非涉及反射,否则有可能防止包C获取包含在包B中的类成员中的包A类的实例。 - nhahtdh
听起来A包和B包应该放在同一个包里,或者需要一些返回私有实现的工厂。 - David
@nhahtdh -> 你能否解释一下如何实现你所说的“防止包C获取属于包B类成员的包A类实例”? - fatfreddyscat
@David -> 你能否解释一下如何制作一个“返回私有实现的工厂”? - fatfreddyscat
@fatfreddyscat:只需确保成员是受保护的、没有修饰符或私有的,并确保您不通过任何方法返回它的实例(让同一包中的类直接使用没有修饰符或受保护的方式访问它)。 - nhahtdh
显示剩余6条评论
2个回答

6
您可以使用JDK 8及其Project Jigsaw来完成。您可能需要查看Project Jigsaw Quickstart Guide
不幸的是,Jigsaw是JDK8的一部分,它还没有完全准备好。预计要到2013年1月才能完全实现功能,并且在2013年中旬之前不会发布。
但是,您已经可以使用JDK 8预览版编译类并使您的想法实现。
在这种情况下,您可以通过将应用程序划分为独立模块来解决问题。您可以尝试做如下操作:
module foo {
    exports foo;
    permits bar;
    permits baz;
}

这里模块foo只能被名为bar或baz的模块所需。其他名称的模块对foo的依赖将无法在编译时、安装时或运行时解决。如果没有许可条款,则没有此类约束。

不确定像OSGI这样的替代框架,如Apache FelixEclipse Equinox是否提供实现这些封装级别的功能。您可能需要进行一些调查。

没有Jigsaw存在的情况下,OSGi的问题在于由框架强制执行的任何规则都可以通过反射来打破。然而,一旦Jigsaw准备好供公众使用,这些规则将由Java本身在编译时、运行时和安装时强制执行,正如您上面所读到的。


谢谢你的好回复(我投了赞成票)。不幸的是,我正在针对Android和JDK 6... :-/ 你还有其他想法吗? - fatfreddyscat
@fatfreddyscat OSGI可能是你最好的选择,但它也不容易实现。 - Edwin Dalorzo
1
请注意,Project Jigsaw 没有被包含在 Java 8 中,现在的目标是 Java 9。 - Jesper

2
你可以通过OSGi实现这一点。Android和JDK 6作为目标在这种情况下不是问题,因为有在Android上运行的OSGi框架,例如mBedded Server for Android。你可以从链接中下载免费的非商业版。
在OSGi中,你有几种选择,具体取决于你想要实现什么。
选项1(推荐): 你可以将软件包A和B放在同一个Bundle AB中,并在此Bundle的清单中仅导出软件包B。软件包/应用程序C或任何其他“用户”应用程序可以导入软件包B并使用它。它无法使用软件包A,因为它对于Bundle AB是内部的。你不需要任何特殊的Java级别声明或依赖项;这将与任何Java版本一起工作,因为模块化和独立的Bundle空间是OSGi基础的一部分,不依赖于最新的Java版本或其他东西。
选项2: 如果出于某种原因你希望将软件包A和B分开放置在不同的Bundle中,则可以这样做,你会在清单中导出和导入软件包,然后通过使用权限(请参见OSGi Permission and Conditional Permission services)控制哪个Bundle有权导入哪个软件包。然而,这更难以实现。
选项3: 你也可以将软件包A放在片段Bundle中,并允许它附加到包含B的Bundle中。通过这种方式,B将有权访问软件包A,但同时你仍然可以在运行时单独更新软件包A。由于片段中的软件包被视为主机Bundle(在这种情况下,主机是包含软件包B的Bundle)的私有内容,因此Bundle C不会看到A。它只会看到Bundle B导出的内容。
由于你对OSGi不太熟悉,我建议你从选项1开始,如果需要的话,稍后可以升级到选项3。
@EdwinDalorzo:绝对不是通过反射来违反OSGi规则。在OSGi中,Bundle有单独的类加载器。你可以像想要的那样从Bundle C反映出类A,唯一的结果是ClassNotFound异常 - 相信我,我已经看到了足够多次;)

非常感谢您详细的回答!对于我的情况,C包需要访问A包和B包;只是我希望B包能够访问A包中的“包私有”类...如果Java支持“友元”概念(如C++中),那么它可以这样工作。我认为这种OSGi方法可能适合我,但由于时间紧迫,我可能只会使用我的“hackjob解决方案”(请参见我原始帖子的“UPDATE”评论),然后在有时间时研究使用您的方法。再次感谢您!投票支持! - fatfreddyscat
如果您想在选项1中仅导出A包的一部分,则也可以这样做。在清单文件中的Export-Package条目中使用“include”或“exclude”导出指令:“include-必须对导入者可见的类名的逗号分隔列表。请注意,值中使用逗号需要将其括在双引号中。有关类过滤,请参见第50页上的类过滤。” - pooh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接