How to Simulate the C++ 'Friend' Concept in Java

Is there a way to simulate the C++ 'friend' concept in Java?

The 'friend' concept is useful in Java, for example, to separate an API from its implementation. It is common for implementation classes to need access to API class internals but these should not be exposed to API clients. This can be achieved using the 'Friend Accessor' pattern as detailed below:

The class exposed through the API:

package api;

public final class Exposed {
static {
// Declare classes in the implementation package as 'friends'
Accessor.setInstance(new AccessorImpl());
}

// Only accessible by 'friend' classes.
Exposed() {

}

// Only accessible by 'friend' classes.
void sayHello() {
System.out.println("Hello");
}

static final class AccessorImpl extends Accessor {
protected Exposed createExposed() {
return new Exposed();
}

protected void sayHello(Exposed exposed) {
exposed.sayHello();
}
}
}

The class providing the 'friend' functionality:

package impl;

public abstract class Accessor {

private static Accessor instance;

static Accessor getInstance() {
Accessor a = instance;
if (a != null) {
return a;
}

return createInstance();
}

private static Accessor createInstance() {
try {
Class.forName(Exposed.class.getName(), true,
Exposed.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}

return instance;
}

public static void setInstance(Accessor accessor) {
if (instance != null) {
throw new IllegalStateException(
"Accessor instance already set");
}

instance = accessor;
}

protected abstract Exposed createExposed();

protected abstract void sayHello(Exposed exposed);
}

Example access from a class in the 'friend' implementation package:

package impl;

public final class FriendlyAccessExample {
public static void main(String[] args) {
Accessor accessor = Accessor.getInstance();
Exposed exposed = accessor.createExposed();
accessor.sayHello(exposed);
}
}

Implementation of Friend concept in Java

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.

C++-like friend class mechanism in Java

If PrivateObject is strongly related to Box why not make it an inner class inside Box?

class Box { 

public static class PrivateObject {
private value;

private increment(){
value++;
}
}

private PrivateObject prv;

public void setPrivateObject(PrivateObject p){
prv = p;
}

public void changeValue(){
prv.increment();
}
}

Now you cannot call increment from outside Box:

public static void main(String args[]) {
Box.PrivateObject obj = new Box.PrivateObject();
Box b = new Box();
b.setPrivateObject(obj);
obj.increment(); // not visible
}

Friends' equivalent for Java?

(Un)fortunately, there is no direct C++ friend equivalent in Java. However, the Java access level modifiers can assist you. In particular, private or package private (AKA package protected, or "default") may help.

Why friend directive is missing in Java?

Here are a few reasons off the top of my head:

  • friend is not required. It is convenient, but not required
  • friend supports bad design. If one class requires friend access to another, you're doing it wrong. (see above, convenient, not required).
  • friend breaks encapsulation. Basically, all my privates are belong to me, and that guy over there (my friend).

Implementing friend (available in C++) functionality in Java

The general solution is to make the methods package-private (which is the default protection level in Java). That way any code in the same package can access them, but not external code.

Java does not allow arbitrary sharing of methods with specific external classes.

EDIT: Protected members are actually less private than package-private. If you have protected members, you can access them from derived classes outside your package, and from any class inside the package. So that may be a solution to your problem - derive the class in another class in the package you want to export to.

Generally, Java considers the package as the main module of encapsulation. The public/protected interface is for classes outside the package, and the default protection level allows access within the package.

If class Outer is my friend, is class Outer::Inner too?

[class.access.nest]/1 states that

A nested class is a member and as such has the same access rights as any other member

So I believe yes, this is standard behavior.

Let's say that Outer has a member function foo(). That function, of course, will have access to Bob's members. To my understanding, the part that I quoted implies that any nested class inside Outer would have the same access rights as foo(), thus having the ability to access Bob's members.

It is also worth noting that the standard contains the following example ([class.friend]/2), note the usage of A::B in Y:

class A {
class B { };
friend class X;
};

struct X : A::B {
// OK: A::B accessible to friend
A::B mx; // OK: A::B accessible to member of friend
class Y {
A::B my; // OK: A::B accessible to nested member of friend
};
};

Friend Package Pattern problems

Finally it works. If you open the class files in the example you see that the Accessor class contains code that is not mentioned in the example. If you insert this code, it works.

private static final Class<?> INIT_API_CLASS = loadClass(
Item.class.getName()
);

private static Class<?> loadClass(String name) {
try {
return Class.forName(name, true, Accessor.class.getClassLoader()
);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}

How to design classes in this use case?

I think I should have mentioned the exact use case I was thinking while writing the question. I updated my question later accordingly.
For my use case, I think aggregation is the right approach instead of inheritance as the classes has has-a relationship instead of is-a.



Related Topics



Leave a reply



Submit