界面可见性限制,但运行良好

时间:2016-05-13 14:44:18

标签: java interface visibility inner-classes

在设计流体API时,有时我想使用接口返回类型,以便调用者可以轻松地遵循可用的方法,或者如果他没有,则会出现编译器错误(Step Builder pattern就是一个例子)。

但是当我想使用接口来指向下一个可用的方法时,例如:

package packageone;

public class SomeClass implements CanPrint {
    private SomeClass() {

    }

    public static CanPrint get() {
        return new SomeClass();
    }

    @Override
    public void print() {
        System.out.println("Runs fine!");
    }
}

public interface CanPrint {//Compile error: must be defined in own file
    public void print();
}

然而,启动快速测试工作正常(以下代码打印“运行正常!”):

package packagetwo;

import packageone.CanPrint;
import packageone.SomeClass;

public class Main {
    public static void main(String[] args) {
        CanPrint returnType = SomeClass.get();
        returnType.print();
    }
}

编译器可以通过包装整个类来“欺骗”:

package packageone;

public class Wrapper {
    public static CanPrint get() {
        return SomeClass.get();
    }

    public static class SomeClass implements CanPrint {

        public static CanPrint get() {
            return new SomeClass();
        }

        private SomeClass() {

        }

        @Override
        public void print() {
            System.out.println("Runs fine!");
        }
    }

    public interface CanPrint {
        public void print();
    }
}

所以当前我希望将接口保持在与打算使用它的唯一代码相同的位置(因为调用者只需要方法),我将它包装在一个仅指向同一方法的包装器类中在内心阶层。

为什么会出现这种限制?另一个类可以导入接口,使用它,甚至可以实现自己的版本。所有这一切似乎没有问题。但它没有编译。

注意:我尽可能地简化了我的示例,但由于这个原因,人们为什么会选择这种设计可能不太清楚。

1 个答案:

答案 0 :(得分:0)

  

为什么会出现这种限制?

它使编译器和人类更容易找到包含顶级类的源文件。

您可以通过以下方式公开界面:

  • 将界面放在单独的文件中,例如ISomeClassCanPrint
  • 或者在返回实例的类中嵌套接口。你的包装器接近这种方法。

这是嵌套界面的另一个例子:

    public static class SomeClassFactory {
      public interface CanPrint {
        public void print();
      }

      public static CanPrint get() {
        return new SomeClass();
      }

      public static class SomeClass implements CanPrint {
        @Override
        public void print() {
          System.out.println("Runs fine!");
        }
      }
    }

更多细节

该限制由Java Language Specification, section 7.6: Top level type declarations

涵盖
  

当且仅当包存储在文件系统(第7.2节)中时,如果在由组成的名称下的文件中找不到类型,则主机系统可以选择强制执行编译时错误的限制。如果满足以下任一条件,则类型名称加上扩展名(例如.java或.jav):

     
      
  • 该类型由声明类型的包的其他编译单元中的代码引用。

  •   
  • 该类型声明为public(因此可以从其他包中的代码访问)。

  •   
     

这个限制意味着每个编译单元最多只能有一个这样的类型。这种限制使Java编译器可以轻松地在包中找到命名类。在实践中,许多程序员选择将每个类或接口类型放在它自己的编译单元中,无论它是公共的还是由其他编译单元中的代码引用。

因此,虽然您可以在单个文件中声明多个顶级类,但最多只能暴露一个文件以外的文件。

但是,您可以根据需要公开任意数量的嵌套类。