【问题标题】:How can a public method return a private type?公共方法如何返回私有类型?
【发布时间】:2014-11-20 04:05:31
【问题描述】:

为什么可以从public 类中的public 方法返回private 嵌套类?编译器不应该抱怨返回类型的可见性低于方法吗?

public final class Outer {
    private static final class Configurator {
        private Configurator() {
        }
    }

    public static Configurator configure() {
        return new Configurator();
    }
}

【问题讨论】:

  • 这是不可能的。请发布一些代码。我假设方法的签名 returns 一个公共接口(或类似的),而该方法的实现返回该接口的私有子类。这是允许的。
  • @Seelenvirtuose:这可能的,令人惊讶。对我来说感觉像是一个 Java 设计缺陷。 (C# 不允许这样做。)
  • 啊,好的。我知道了。简单的技术说明:在此方法的声明范围内,(私有)类是可见的,因此可以用作方法签名中的返回类型。但是,您只能从此类内部调用此方法。由于无法访问该私有类,其他所有类中的代码都无法调用此方法。
  • @JonSkeet 那么如果我从外部包调用configure() 方法会发生什么?方法会不可见吗?
  • @JonSkeet 你是对的。我是从客户的的角度考虑的。

标签: java oop visibility


【解决方案1】:

可以从类外部调用这样的方法,但前提是您乐于丢弃结果。

public class TestClass {
  public static void main(String[] args) throws Exception {
    Outer.configure(); // this is valid
  }
}

或者如果您愿意将结果称为Object

public class TestClass {
  public static void main(String[] args) throws Exception {
    Object o = Outer.configure(); // this is valid
  }
}

编译器允许这样做,因为它不会破坏 Java 的任何规则。 Object 是您的私有类中唯一公开的超类。

我怀疑这种模式有很多实际用途。如果您希望返回一个不透明的对象,最好通过传回一个没有公共方法的公共类来完成,因为您至少可以在将其传回需要使用它的类时进行类型检查。

【讨论】:

  • +1 - 实际上,您的第二个示例说明了这种(看似)异常语言功能的用途。
  • @Holger 除非我误解了您的观点,否则这正是我在回答的最后一段中所说的。这种模式可以用来创建不透明的对象,但是有更好的方法来做到这一点。
  • @Holger 我认为我们谈论的是略有不同的事情。你说的是一般情况,而我更多地关注这个具体情况(即Object 是唯一的公共超类)。因此,我们的反应不同。不过,当你提出一些有效的观点时,你会得到我的 +1。
  • @Duncan:是的,我在这里比较笼统,因为 OP 的问题是“为什么有可能……”。但事实上,在这个具体的例子中,使用这种可能性几乎没有意义。
【解决方案2】:

有一个严格的要求,interface 方法必须是public。因此,当返回非public 类型的方法满足interface 合同时,它必须是 public

class Foo implements Supplier<NonPublicType> {
  public NonPublicType get() { // must be public !
    …
  }
}

此外,如果声明的类和方法是public,仍然可以从包外部调用此方法。但是,如果要使用结果,则必须将结果分配给非public 类的可访问超类型。例如。如果上面示例中的NonPublicType 实现了CharSequence,您可以在包外说CharSequence cs=foo.get();(如果我们将Foo 更改为public)。

请注意,有问题的方法可能会覆盖返回 public 类型并返回更具体的非public 类型(又名Covariant return type)的超类方法。该方法可能会从同一包中的类中调用,并使用更具体的类型。

【讨论】:

    【解决方案3】:

    嵌套类是其封闭类的成员,因此就像在单例设计模式中一样,当您调用公共方法仅通过该方法获取私有静态实例时,也可以将私有静态类调用为成员或其方法之一。 您嵌套它的原因可能是以下之一:

    • 对仅在一处使用的类进行逻辑分组
    • 增加封装
    • 获得更具可读性和可维护性的代码

    例如,如果它是私有的,你不能说:

    OuterClass.StaticNestedClass nestedObject =
     new OuterClass.StaticNestedClass();
    

    因此,在这种情况下,您需要一个公共方法来访问它。

    【讨论】:

      【解决方案4】:

      这是完全有可能的并且是有道理的,尽管一个简短的实际例子相当困难。可以返回私有类来代替它的任何超类。这里提到了对象,但它不一定是对象。这个例子有点古怪,但它确实有效。

      abstract class Super {
          abstract void zipper();
      }
      
      final class Outer {
      
          private static final class Configurator extends Super {
              private Configurator() {
              }
      
              @Override
              void zipper() {
                  System.out.println("Zip!");
              }
              public void zoomer() {
                  System.out.println("Zoom!");
              }
      
          }
      
          public static Configurator configure() {
              return new Configurator();
          }
      
      }
      
      public final class PublicPrivate {
      
          public static void main(final String[] args) {
              /* Outer.configure returns an instance of Configurator, 
                  a subclass of Super */
              final Super = Outer.configure();
              /* Configurator.zoomer() is not available, 
                  because Configurator is private */
              // o.zoomer(); /* Uncomment this line and the compile will fail */
              /* But Super.zipper() is available,
                  in the form in which Configurator overrid it */
              o.zipper();
          }
      
      }
      

      【讨论】:

        【解决方案5】:

        我认为这是因为您将如何处理被调用者中的对象可以推迟到最后一刻。例如,假设

        private static class X{
        
        }
        
        public static MyClass.X getX() {
            return new MyClass.X();
        }
        

        然后从另一个班级,我做类似的事情

        public void get() {
            System.out.println(MyClass.getX());
        }
        

        这里我不需要知道 X 是什么类型,因为 println 只想要它的 toString(),它继承自 Object。

        另一个例子

        private static class X implements Serializable{
        
        }
        
        public static MyClass.X getX() {
            return new MyClass.X();
        }
        

        在这种情况下,我不知道 X,但我知道它是可序列化的,所以我可以做类似的事情

        public void get() {
            Serializable x= MyClass.getX();
        }
        

        当然,我不能使用 MyClass 外部的类型 X,但在我真正需要它之前,它只是 Object 的另一个实例

        【讨论】:

          猜你喜欢
          • 2014-01-03
          • 2013-04-05
          • 2011-07-21
          • 1970-01-01
          • 2011-12-08
          • 1970-01-01
          • 2022-01-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多