Is there a way to simulate the C++ 'friend'

2019-01-01 02:58发布

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?

18条回答
只靠听说
2楼-- · 2019-01-01 03:24

I think that friend classes in C++ are like inner-class concept in Java. Using inner-classes you can actually define an enclosing class and an enclosed one. Enclosed class has full access to the public and private members of it's enclosing class. see the following link: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

查看更多
闭嘴吧你
3楼-- · 2019-01-01 03:24

Not using a keyword or so.

You could "cheat" using reflection etc., but I wouldn't recommend "cheating".

查看更多
骚的不知所云
4楼-- · 2019-01-01 03:25

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, and 3.

查看更多
呛了眼睛熬了心
5楼-- · 2019-01-01 03:28

I agree that in most cases the friend keyword is unnecessary.

  • Package-private (aka. default) is sufficient in most cases where you have a group of heavily intertwined classes
  • For debug classes that want access to internals, I usually make the method private and access it via reflection. Speed usually isn't important here
  • Sometimes, you implement a method that is a "hack" or otherwise which is subject to change. I make it public, but use @Deprecated to indicate that you shouldn't rely on this method existing.

And finally, if it really is necessary, there is the friend accessor pattern mentioned in the other answers.

查看更多
与君花间醉酒
6楼-- · 2019-01-01 03:30

A method I've found for solving this problem is to create an accessor object, like so:

class Foo {
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* This is the accessor. Anyone with a reference to this has special access. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    /** You get an accessor by calling this method. This method can only
     * be called once, so calling is like claiming ownership of the accessor. */
    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }
}

The first code to call getAccessor() "claims ownership" of the accessor. Usually, this is code that creates the object.

Foo bar = new Foo(); //This object is safe to share.
FooAccessor barAccessor = bar.getAccessor(); //This one is not.

This also has an advantage over C++'s friend mechanism, because it allows you to limit access on a per-instance level, as opposed to a per-class level. By controlling the accessor reference, you control access to the object. You can also create multiple accessors, and give different access to each, which allows fine-grained control over what code can access what:

class Foo {
    private String secret;
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* Normal accessor. Can write to locked, but not read secret. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }

    /* Super accessor. Allows access to secret. */
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    private FooSuperAccessor superAccessor;

    public FooSuperAccessor getAccessor() {
        if (superAccessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return superAccessor = new FooSuperAccessor();
    }
}

Finally, if you'd like things to be a bit more organized, you can create a reference object, which holds everything together. This allows you to claim all accessors with one method call, as well as keep them together with their linked instance. Once you have the reference, you can pass the accessors out to the code that needs it:

class Foo {
    private String secret;
    private String locked;

    public String getLocked() { return locked; }

    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    public class FooReference {
        public final Foo foo;
        public final FooAccessor accessor;
        public final FooSuperAccessor superAccessor;

        private FooReference() {
            this.foo = Foo.this;
            this.accessor = new FooAccessor();
            this.superAccessor = new FooSuperAccessor();
        }
    }

    private FooReference reference;

    /* Beware, anyone with this object has *all* the accessors! */
    public FooReference getReference() {
        if (reference != null)
            throw new IllegalStateException("Cannot return reference more than once!");
        return reference = new FooReference();
    }
}

After much head-banging (not the good kind), this was my final solution, and I very much like it. It is flexible, simple to use, and allows very good control over class access. (The with reference only access is very useful.) If you use protected instead of private for the accessors/references, sub-classes of Foo can even return extended references from getReference. It also doesn't require any reflection, so it can be used in any environment.

查看更多
呛了眼睛熬了心
7楼-- · 2019-01-01 03:34

The designers of Java explicitly rejected the idea of friend as it works in C++. You put your "friends" in the same package. Private, protected, and packaged security is enforced as part of the language design.

James Gosling wanted Java to be C++ without the mistakes. I believe he felt that friend was a mistake because it violates OOP principles. Packages provide a reasonable way to organize components without being too purist about OOP.

NR pointed out that you could cheat using reflection, but even that only works if you aren't using the SecurityManager. If you turn on Java standard security, you won't be able to cheat with reflection unless you write security policy to specifically allow it.

查看更多
登录 后发表回答