可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is it really impossible to hide some classes in a jar file?
I wanted not to allow direct instantiation of the classes to keep it more flexible. Only the factory (or a facade) should be visible of this jar.
Is there any other way than solve this problem than creating two projects?
(Two projects: the first one contains the classes (implementation) and the other one references to the first one and contains the factory; later only the second one will be referenced)
回答1:
I think you will have either compiler failure or warning if your public factory method try to return something which is "hidden".
No, you can not hide a public class without reimplementing your own ClassLoader
or using OSGi or anything similar.
What you can do is to separate interface api from the implementation, e.g. have one project which contains only the interfaces and another porject which contains the implmentations. However, you still cannot hide the implementation classes.
回答2:
I'm understanding you're not looking to hide the actual classes, just prevent their construction outside a factory class. This I think can be quite easily achieved by using package private (default) visibility in the class constructors. The only limitation is that you'll need to have the classes and the factory in the same package so in a medium to large codebase things may get unnecessarily complex.
回答3:
If I understand your question correctly, you would like to make sure that users of your library are forced to use your factory to instantiate their objects rather than using the constructors themselves.
As I see it there are two possibilities, one of which is silly but usable in few, specific cases, and the other one is the most practical and probably most commonly used way of doing it.
- You could make all your classes into
private inner classes of the
factory. This would work if you had
one factory per class, but is hardly
workable if you have a lot of
different classes being managed
through one factory.
- You could use the
protected
access modifier to
restrict access to your class
constructors. This is common
practice when using the factory
pattern.
回答4:
Obfuscation can help you somehow.
回答5:
With standard classloaders an plain old jar files this is not possible. OSGi cas this concept of making visible to an other bundle only some packages and not other (i.e. separation of public api and internal implementation).
If you are usinf eclipse you may enforce such rules with this
回答6:
If I understand you correctly when you say "not to allow direct instantiation of the classes to keep it more flexible", a properly executed facade pattern will handle this.
Restrict the constructors of all the classes you want to hide to package scope. Open the facade class to public scope.
http://mindprod.com/jgloss/packagescope.html
"If you have a variable or method in
your class that you don’t want clients
of your class directly accessing,
don’t give it a public, protected or
private declaration. Due to an
oversight in the design of Java, you
can’t explicitly declare the default
“package” accessibility. Other members
of the package will be able to see it,
but classes outside the package that
inherit from yours, won’t. The
protected accessibility attribute
offers slightly more visibibily. A
protected method is visible to
inheriting classes, even not part of
the same package. A package scope
(default) method is not. That is the
only difference between protected and
package scope. "
回答7:
There are two solutions to your question that don't involve keeping all classes in the same package.
The first is to use the Friend Accessor/Friend Package pattern described in (Practical API Design, Tulach 2008).
The second is to use OSGi. There is an article here explaining how OSGi accomplishes this.
Related Questions: 1, 2, 3, and 4.
回答8:
You can do such magics with a custom class loader but:
- the correct separation will be available only in a project staffed with your class loader;
- it's really doubtful that the effort to create such loader is worthy.
In such situations I would do something similar to what we may see in the standard Java. E.g.you see javax.xml.stream.XMLInputFactory
but somewhere you have com.sun.xml.internal.stream.XMLInputFactoryImpl
. It is perfectly compilable if you write:
new com.sun.xml.internal.stream.XMLInputFactoryImpl()
though you will hardly do it :-) With a system property you may control the actual implementation that is being loaded. To me such approach is fine in many situations.
I hope I have understood your question correctly ;)
Cheers!