【问题标题】:Private class as return type from public method私有类作为公共方法的返回类型
【发布时间】:2014-01-03 20:21:53
【问题描述】:

为什么这是有效的?

Foo.java

public class Foo {

    public Bar getBar() {
        return new Bar();
    }

    private class Bar {}

}

如果 Bar 是私有的,这个类的用户将如何使用这个方法?多态当然可以使用,但是这不应该是无效的,并且声明应该表明这是返回一个Object吗?

【问题讨论】:

    标签: java polymorphism access-modifiers


    【解决方案1】:

    我有一个完全有效的用例,我很高兴这是允许的。

    让我们继续拥有一个创建 UI 片段的类。它接受某种领域对象并创建一个 UI:

    public Node createPersonUI(Person person) {
        BasePanel panel = new BasePanel();
        // ... setup panel with values ...
        return panel;
    }
    

    BasePanelNode 的子类,并且是调用者没有业务的一些内部类,因为这个类决定了事情的外观。

    现在,当我需要支持一个新对象 PersonalProfile 时,我发现自己需要重用这个类的一部分,它包含更多信息,但也包含基本的 Person 数据:

    public Node createPersonalProfileUI(PersonalProfile profile) {
        BasePanel panel = new BasePanel();
        // ... setup panel with values ...
        return panel;
    }
    

    但是,该代码部分重复,所以我这样做了:

    public Node createPersonalProfileUI(PersonalProfile profile) {
        BasePanel panel = (BasePanel)createPerson(profile.getPerson());
        // ... only setup missing values ...
        return panel;
    }
    

    然而,演员阵容有点荒谬——将其更改为返回 BasePanel 不仅有效,而且不会暴露我的私有类的任何功能。相反,它只公开它继承自的任何公共类的方法......太棒了!

    完整代码:

    public BasePanel createPersonUI(Person person) {
        BasePanel panel = new BasePanel();
        // ... setup panel with values ...
        return panel;
    }
    
    public BasePanel createPersonalProfileUI(PersonalProfile profile) {
        BasePanel panel = createPerson(profile.getPerson());
        // ... only setup missing values ...
        return panel;
    }
    
    private class BasePanel extends Node {
    }
    

    【讨论】:

    • 我想在这种情况下,使用第三个私有方法来封装该功能会更正确。基本上 createPersonUI 已经在做,并且 createPersonUI 只需调用这个新的私有方法。我认为,例如,当为此类生成文档时,该方法的返回类型将是 BasePanel,并且文档的使用者可能无法知道 BasePanel 是什么,或者它甚至扩展了 Node,因为它是私人的。但是好吧,在所有这些场景中,如果工具意识到这一点并改为显示 Node,它就不会那么糟糕。
    【解决方案2】:

    我刚刚对此进行了一些研究,但未能找到明确的答案。这似乎很可能只是 Java 语言设计者的疏忽,因为它实际上并没有造成任何伤害,所以它被留下了。这与将public 方法放入private 类没有什么不同。没有什么能阻止你这样做,即使没有办法实际访问 public 方法。

    当您尝试执行此操作时,当然 NetBeans 会给您“通过公共 API 导出非公共类型”的警告。我希望大多数其他环境会给出类似的警告。

    返回的对象对于任何尝试使用它的人来说完全没用(除非他们使用反射),他们所能做的几乎就是将其存储到Object(或他们有权访问的任何其他超类)中然后传递Object

    如果传递的Object 被用作传递但从未操作过的“句柄”,您可能希望这样做。在这种情况下,尽管拥有 public 类仍然更有意义,但将其内的所有方法设为 private 以防止它们在您的类之外被执行(或定义一个 public 接口以返回并拥有private 类实现)。

    所以答案似乎是:

    它可能不应该是有效的,但由于它不会造成任何伤害,它从未被阻止。

    在类似的主题上有一个很好的答案:

    Exporting non-public type through public API

    【讨论】:

    • 关于在私有类中创建公共方法的说法。确实有所不同,如果您在父类中有该私有类的对象,则不能调用私有方法,对吗?因此,私有类中的私有/公共方法对于定义可访问该类的作用域中的用法很有用。
    • 其实可以的。内部类算作父类的成员,用于访问私有方法和变量(从内部到外部以及从外部到内部)。
    • 其实很重要。它允许您使用私有方法创建内部类并从外部类访问它们,而不必在该类之外公开它们。
    • 实际上,只要类实现了某些接口,在适当设计的 Java 代码中返回私有类是非常常见和有用的。 Java 库一直都在这样做。尽管他们可能会返回接口类型而不是私有类类型。
    • 没错。返回类型是接口而不是实现它的私有类。返回私人课程很有意义。没有私有类的返回类型。
    【解决方案3】:

    为什么这是有效的?

    因为调用处的客户端代码可能期待Object(或根本不期待任何东西),所以从任何地方调用此方法都没有问题:

    Object o = new Foo().getBar();
    

    【讨论】:

      【解决方案4】:

      它是嵌套类型的一种形式。这种类声明称为内部类。如果您将内部类声明为静态,那么它将被称为顶级嵌套类。 Java 中可用的其他形式的嵌套类型是本地类;在块中声明和定义的类,即方法或构造函数或初始化块。嵌套类型的第四种形式是匿名类;一个没有任何名称的类,其对象在定义该类的地方使用。

      就您的情况而言,即内部类,一个类中的所有类都可以用公共、私有和受保护的访问说明符声明。包含在封闭类中的所有类以及封闭类本身共享信任关系。这意味着,内部类的所有私有成员以及封闭类的私有成员都是相互共享的。但是,如果没有封闭类的对象,您将无法访问内部类的对象。

      当您尝试创建内部类的对象时,编译器会报告编译时错误。但是下面的示例访问每个其他类的私有成员,即封闭类访问内部类的私有成员和内部类访问封闭类的私有成员:

      class Bar {
          private static int x;
      
          public void getFoo() {
              System.out.println(new Foo().y);
          }
      
          private class Foo {
              private int y;
              public void getBar() {
                  System.out.println(Bar.x);
              }
          }
      }
      
      public class Test{
          public static void main(String[] a) {
              Bar b = new Bar();
              //Bar.Foo f = new Bar.Foo(); This is completely illegal syntax.     
          }
      }
      

      内部类的最佳示例是作为封闭类的Accounts 类和作为内部类的Transaction 类之间的关系。一个Accounts 类可以有多个Transaction 对象,但没有Accounts 对象就不能存在Transaction 对象。

      尽管返回私有内部类的对象是无用的,因为它在其类之外变得不可见。正如上面AccountsTransaction 类的示例所解释的。没有Accounts 对象,Transaction 就无法存在。

      【讨论】:

        【解决方案5】:
        public class Foo {
        
            private String myString;
        
            public String getMyString() {
                return myString;
            }
        
        }
        

        这也是有效的。为什么内部类的行为应该不同?

        制作Barprivate只会使其对外界不可见,就像制作字段private一样。

        一个重要的警告是,即使您能够在 Foo 对象上调用 getBar(),您也不能调用该引用的方法(因为可见性)。

        所以主要的是你可以这样做,但你不应该这样做。

        我能想象的唯一情况是Foo也是一个内部类,而Foo的外部类想使用Bar

        【讨论】:

        • 我投了反对票。我回答的时候你还没有回答。
        • 我回答的时候你还没有回答。你是认真的吗?
        • @Adam Aroid 很好的解释。我不知道为什么有人投了反对票???我投了赞成票,因为我喜欢你的回答。
        • 也许我读得太快了,因为我不记得看到最后一行 - 这是唯一直接回答问题的部分。对此感到抱歉,我确实在重新阅读后立即删除了反对票:)
        • 问题是关于嵌套类型的可见性。与变量无关。
        【解决方案6】:

        返回私有类的公共方法可能很有用,因为您需要能够从任何范围调用该方法(例如,改变 Foo 的内部状态),如果您还需要任何类型的结果,则用于内部使用简单地调用方法。

        【讨论】:

          【解决方案7】:

          这是有效的,因为 BarFoo 类可见。这样就可以编译了。

          当然另一个类看不到Bar,因此不能使用返回值。

          但是另一个类仍然可以直接调用方法而不使用返回值。

          public class FooBar {
          
             public void invokeBar() {
                  Foo foo = new Foo();
                  foo.getBar();
             }
          }
          

          【讨论】:

          • @JqueryLearner 好的,我更新了,以免混淆其他人。对于命名空间,我只是指名称中的空格是有效的。你怎么称呼它?
          • 我也研究过 c#,所以知道命名空间在 c# 中的含义
          • @JqueryLearner 我相信你知道。我只是问你是否知道它在java中通常是如何调用的。我认为命名空间对 java 也有好处。
          【解决方案8】:

          内部类

          内部类代表一种特殊类型的关系,它可以访问外部类的所有成员(数据成员和方法),包括私有的。嵌套类可以导致代码更具可读性和可维护性,因为它在逻辑上仅将类分组到一个位置。

          【讨论】:

            猜你喜欢
            • 2014-11-20
            • 2013-04-05
            • 1970-01-01
            • 2011-07-21
            • 2019-01-26
            • 2011-12-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多