I would like to be able to write a Java class in one package which can access non-public methods of a class in another package without having to make it a subclass of the other class. Is this possible?
相关问题
- Delete Messages from a Topic in Apache Kafka
- Jackson Deserialization not calling deserialize on
- Sorting 3 numbers without branching [closed]
- How to maintain order of key-value in DataFrame sa
- StackExchange API - Deserialize Date in JSON Respo
I prefer delegation or composition or factory class (depending upon the issue that results in this problem) to avoid making it a public class.
If it is a "interface/implementation classes in different packages" problem, then I would use a public factory class that would in the same package as the impl package and prevent the exposure of the impl class.
If it is a "I hate to make this class/method public just to provide this functionality for some other class in a different package" problem, then I would use a public delegate class in the same package and expose only that part of the functionality needed by the "outsider" class.
Some of these decisions are driven by the target server classloading architecture (OSGi bundle, WAR/EAR, etc.), deployment and package naming conventions. For example, the above proposed solution, 'Friend Accessor' pattern is clever for normal java applications. I wonder if it gets tricky to implement it in OSGi due to the difference in classloading style.
eirikma's answer is easy and excellent. I might add one more thing: instead of having a publicly accessible method, getFriend() to get a friend which cannot be used, you could go one step further and disallow getting the friend without a token: getFriend(Service.FriendToken). This FriendToken would be an inner public class with a private constructor, so that only Service could instantiate one.
Here's a clear use-case example with a reusable
Friend
class. The benefit of this mechanism is simplicity of use. Maybe good for giving unit test classes more access than the rest of the application.To begin, here is an example of how to use the
Friend
class.Then in another package you can do this:
The
Friend
class is as follows.However, the problem is that it can be abused like so:
Now, it may be true that the
Other
class doesn't have any public constructors, therefore making the aboveAbuser
code impossible. However, if your class does have a public constructor then it is probably advisable to duplicate the Friend class as an inner class. Take thisOther2
class as an example:And then the
Owner2
class would be like this:Notice that the
Other2.Friend
class has a private constructor, thus making this a much more secure way of doing it.If you want to access protected methods you could create a subclass of the class you want to use that exposes the methods you want to use as public (or internal to the namespace to be safer), and have an instance of that class in your class (use it as a proxy).
As far as private methods are concerned (I think) you are out of luck.
Here is a small trick that I use in JAVA to replicate C++ friend mechanism.
Lets say I have a class
Romeo
and another classJuliet
. They are in different packages (family) for hatred reasons.Romeo
wants tocuddle
Juliet
andJuliet
wants to only letRomeo
cuddle
her.In C++,
Juliet
would declareRomeo
as a (lover)friend
but there are no such things in java.Here are the classes and the trick :
Ladies first :
So the method
Juliet.cuddle
ispublic
but you need aRomeo.Love
to call it. It uses thisRomeo.Love
as a "signature security" to ensure that onlyRomeo
can call this method and simply callshashCode
on it so the runtime will throw aNullPointerException
if it isnull
.Now boys :
The class
Romeo.Love
is public, but its constructor isprivate
. Therefore anyone can see it, but onlyRomeo
can construct it. I use a static reference so theRomeo.Love
that is never used is only constructed once and does not impact optimization.Therefore,
Romeo
cancuddle
Juliet
and only he can because only he can construct and access aRomeo.Love
instance, which is required byJuliet
tocuddle
her (or else she'll slap you with aNullPointerException
).The provided solution was perhaps not the simplest. Another approach is based on the same idea as in C++: private members are not accessible outside the package/private scope, except for a specific class that the owner makes a friend of itself.
The class that needs friend access to a member should create a inner public abstract "friend class" that the class owning the hidden properties can export access to, by returning a subclass that implement the access-implementing methods. The "API" method of the friend class can be private so it is not accessible outside the class that needs friend access. Its only statement is a call to an abstract protected member that the exporting class implements.
Here's the code:
First the test that verifies that this actually works:
Then the Service that needs friend access to a package private member of Entity:
Finally: the Entity class that provides friendly access to a package private member only to the class application.service.Service.
Okay, I must admit it is a bit longer than "friend service::Service;" but it might be possible to shorten it while retaining compile-time checking by using annotations.