当前位置:  开发笔记 > 编程语言 > 正文

有没有办法在Java中模拟C++的"朋友"概念?

如何解决《有没有办法在Java中模拟C++的"朋友"概念?》经验,为你挑选了5个好方法。

我希望能够在一个包中编写一个Java类,它可以访问另一个包中的类的非公共方法,而不必使它成为另一个类的子类.这可能吗?



1> Salomon BRYS..:

这是我在JAVA中用来复制C++好友机制的一个小技巧.

让我们说我有一个班级Romeo和另一个班级Juliet.出于仇恨原因,他们在不同的包裹(家庭)中.

Romeo想要cuddle Juliet而且Juliet只想让Romeo cuddle她.

在C++中,Juliet将声明Romeo为(情人)friend但在java中没有这样的东西.

以下是课程和技巧:

女士优先 :

package capulet;

import montague.Romeo;

public class Juliet {

    public static void cuddle(Romeo.Love love) {
        Objects.requireNonNull(love);
        System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
    }

}

所以方法Juliet.cuddle是,public但你需要Romeo.Love调用它.它使用它Romeo.Love作为"签名安全性"来确保只能Romeo调用此方法并简单地调用NullPointerException它,以便运行时将抛出一个nullif Romeo.Love.

现在男孩:

package montague;

import capulet.Juliet;

public class Romeo {
    public static final class Love { private Love() {} }
    private static final Love love = new Love();

    public static void cuddleJuliet() {
        Juliet.cuddle(love);
    }
}

该类private是公共的,但它的构造函数是Romeo.因此任何人都可以看到它,但只能Romeo.Love构建它.我使用静态引用,因此Romeo从未使用它只构造一次,不会影响优化.

因此,cuddleJuliet Romeo.Love只有他能,因为只有他可以构建和访问一个Juliet实例,这是必需的cuddle,以NullPointerException她的(否则她会拍你一个Romeo).


+1"用你的NullPointerException打你".非常令人印象深刻.
你可以通过将`love`字段改为`final` ;-)来为`Julia`永久制作'Romeo`的'Love`.
对于幽默和伟大的例子,所有答案都应该像这个(Y)+1.
@Matthias爱情领域是静态的......我将编辑答案以使其成为最终;)
@Steazy有:查找NotNull,NonNull和CheckForNull注释.请查看IDE的文档,了解如何使用和强制执行这些注释.我知道IntelliJ默认嵌入了这个,并且eclipse需要一个插件(比如FindBugs).
让这更优雅(虽然可能也令人困惑)的另一个很酷的伎俩是将`love`字段重命名为`Love`.是的,您实际上可以这样做(请参阅http://stackoverflow.com/a/14027255/1084488).结果将是"罗密欧"代码中提到的"爱情"(例如在`Juliet.cuddle(Love);`)将被解释为对*他的*永恒,一个"爱"对象的引用(! ),而在"罗密欧"课程之外提到的"罗密欧.爱情"将引用公共的"爱"类(!).
完全同意,不仅这个答案是一个很好的答案,但它提供了一个简单而有效的背景来记住这个"模式".很好.
“我的代码是诗歌”一词的全新含义
我读过的最奇妙的答案。+1通过“最终”预选赛使罗密欧的爱情永恒;)

2> David G..:

Java的设计者明确拒绝了朋友的想法,因为它在C++中有效.你把你的"朋友"放在同一个包里.私有,受保护和打包的安全性是语言设计的一部分.

James Gosling希望Java能够成为C++而不会出错.我相信他觉得朋友是个错误,因为它违反了OOP原则.包提供了一种合理的方式来组织组件而不会过于纯粹的OOP.

NR指出你可以使用反射作弊,但即使只有你没有使用SecurityManager才有效.如果打开Java标准安全性,除非您编写安全策略以明确允许,否则您将无法使用反射作弊.


如果Gosling真的认为"朋友"违反了OOP(特别是超过包访问权限)那么[他真的不明白](http://www.parashift.com/c++-faq-lite/friends.html #faq-14.2)(完全可能,很多人误解了它).
我并不是说是一个学究,但访问修饰符不是一种安全机制.
有时需要分离类组件(例如,实现和API,核心对象和适配器).包级保护同时过于宽松,限制性太强,无法正确执行此操作.
访问修饰符是java安全模型的一部分.我特意指的是用于反射的java.lang.RuntimePermission:accessDeclaredMembers和accessClassInPackage.
@GregD从某种意义上说,它们可以被视为安全机制,可以帮助防止开发人员错误地使用类成员。我认为最好将它们称为“安全机制”。

3> Matthew Murd..:

"朋友"概念在Java中很有用,例如,将API与其实现分开.实现类通常需要访问API类内部,但这些不应该暴露给API客户端.这可以使用'Friend Accessor'模式实现,详情如下:

通过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();
        }
    }
}

提供"朋友"功能的课程:

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);
}

来自'friend'实现包中的类的示例访问:

package impl;

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


我的Java相当生疏,所以请原谅我的无知.这与Salomon BRYS发布的"罗密欧与朱丽叶"解决方案相比有什么优势?如果我在代码库中偶然发现它(没有附加解释,即重评论),这种实现会吓跑我的裤子.罗密欧与朱丽叶的方法很容易理解.

4> Jeff Axelrod..:

您的问题有两种解决方案,不涉及将所有类保留在同一个包中.

第一种是使用(Practical API Design,Tulach 2008)中描述的Friend Accessor/Friend Package模式.

第二是使用OSGi.有文章在这里解释的OSGi是如何实现这一点.

相关问题:1,2,和3.



5> Black..:

据我所知,这是不可能的.

也许,你可以给我们一些关于你的设计的更多细节.像这样的问题可能是设计缺陷的结果.

考虑一下

如果这些课程密切相关,为什么这些课程在不同的课程中呢?

有A访问B的私人成员还是应该将操作移到B类并由A触发?

这是真正的呼唤还是事件处理更好?

推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有