Implementation of Friend concept in Java
Solution 1
Java does not have the friend keyword from C++. There is, however, a way to emulate that; a way that actually gives a lot more precise control. Suppose that you have classes A and B. B needs access to some private method or field in A.
public class A {
private int privateInt = 31415;
public class SomePrivateMethods {
public int getSomethingPrivate() { return privateInt; }
private SomePrivateMethods() { } // no public constructor
}
public void giveKeyTo(B other) {
other.receiveKey(new SomePrivateMethods());
}
}
public class B {
private A.SomePrivateMethods key;
public void receiveKey(A.SomePrivateMethods key) {
this.key = key;
}
public void usageExample() {
A anA = new A();
// int foo = anA.privateInt; // doesn't work, not accessible
anA.giveKeyTo(this);
int fii = key.getSomethingPrivate();
System.out.println(fii);
}
}
The usageExample() shows how this works. The instance of B doesn't have access to the private fields or methods of an instance of A. But by calling the giveKeyTo(), class B can get access. No other class can get access to that method, since it a requires a valid B as an argument. The constructor is private.
The class B can then use any of the methods that are handed to it in the key. This, while clumsier to set up than the C++ friend keyword, is much more fine-grained. The class A can chose exactly which methods to expose to exactly which classes.
Now, in the above case A is granting access to all instances of B and instances of subclasses of B. If the latter is not desired, then the giveKeyTo() method can internally check the exact type of other with getClass(), and throw an exception if it is not precisely B.
Solution 2
Suppose A.foo()
should only be called by B
. This can be arranged by a token that can only be generated by B
.
public class B
{
public static class ToA { private ToA(){} }
private static final ToA b2a = new ToA();
void test()
{
new A().foo(b2a);
}
}
public class A
{
public void foo(B.ToA b2a)
{
if(b2a==null)
throw new Error("you ain't B");
// ...
}
}
Only B
can generate a non-null B.ToA
token. If both A
and B
do not leak this token to the 3rd party,
nobody else can invoke A.foo()
If A2
wants to friend B
too, it needs a different token type. If it's the same token type, since A
got a token of the type from B
, A
can pretend to be B
to A2
.
The check is done at runtime, not compile time, that is not perfect. Not a big deal though, since any 3rd party can only invoke A.foo()
with a null
, it can't be an innocent mistake which we want to check at compile time; it's probably malicious so we don't care to warn the caller at compile time.
Solution 3
In Java you can put both (or more) classes into the same package. All methods and fields with the protected
qualifier can directly be accessed by all classes in that package.
FirstName LastName
Updated on July 09, 2022Comments
-
FirstName LastName almost 2 years
How does one implement the friend concept in Java (like C++)?
-
FirstName LastName over 11 yearsIndeed, however if there is already existing legacy code in place and refactoring is going to be a hassle and you don't want to change the visibility of fields and methods, then setting up such a friend fuctionality might be an option. I agree, it will not be highly readable or even recommended, but it works as a quick fix.
-
Tony Hopkinson over 11 yearsI feel the need to point out this a last ditch, out of options, worst case, welcome to sub-prime levels of technical debt, when it become permanently temporary, or temporarily permanent option.
-
AlexWien over 11 yearsNearly all people that I know would just define the affected methods as public, but your approach is interesting
-
Tony Hopkinson over 11 years@AlexWien I want to hurt all those people ...
-
AlexWien over 11 years@TonyHopkinson it also depends if it will be a public API used by some people, or if that code will only be used by two people for the rest of the program life.
-
Tony Hopkinson over 11 yearsFor Life? The only thing that doesn't change in IT, is things keep changing...
-
Florian over 10 yearsThey can also be accessed by all classes in other packages that derive from that class. Package access and protected access are not the same in Java. Package level access is not regulated by the
protected
keyword. Actually, usingprotected
broadens the package-private access level. -
filpa over 7 yearsVery nice. A better way to implement this would perhaps be to declare class
B
asfinal
, avoiding the potential security risks. -
Ignacio Baca almost 5 yearsI think this is the same that: if you want to share a minimal part of api between A and B, just extract C. At least this is what you have done in practice. At that point, I will move the field to C as keeping it in A has no advantage (at least I don't see it).
-
Maik over 2 yearsI don't know, why this is done voted. For me it is a valid way. But I would throw an adequate exception instead of returning null. In addition I would use ' Thread.currentThread().getStackTrace()' instead of 'Throwable().getStackTrace()'. Further I would not write 'new String("example.java.testing.B")' but 'example.java.testing.B.class.getName()'. (can be found by reference search)
-
Maik over 2 years