Implementation of Friend concept in Java

49,888

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.

Share:
49,888
FirstName LastName
Author by

FirstName LastName

Updated on July 09, 2022

Comments

  • FirstName LastName
    FirstName LastName almost 2 years

    How does one implement the friend concept in Java (like C++)?

  • FirstName LastName
    FirstName LastName over 11 years
    Indeed, 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
    Tony Hopkinson over 11 years
    I 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
    AlexWien over 11 years
    Nearly all people that I know would just define the affected methods as public, but your approach is interesting
  • Tony Hopkinson
    Tony Hopkinson over 11 years
    @AlexWien I want to hurt all those people ...
  • AlexWien
    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
    Tony Hopkinson over 11 years
    For Life? The only thing that doesn't change in IT, is things keep changing...
  • Florian
    Florian over 10 years
    They 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, using protected broadens the package-private access level.
  • filpa
    filpa over 7 years
    Very nice. A better way to implement this would perhaps be to declare class B as final, avoiding the potential security risks.
  • Ignacio Baca
    Ignacio Baca almost 5 years
    I 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
    Maik over 2 years
    I 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
    Maik over 2 years