在编译时有条件地删除Java方法

Fle*_*eon 11 java preprocessor processor conditional-compilation

我试图实现类似于C#预处理器的东西.我知道Java没有相同的预处理器功能,并且我知道有很多方法可以使用Factory等设计模式来实现类似的结果.但是,我仍然有兴趣找到这个问题的解决方案.

目前,我所做的是创建一个包含几个静态最终布尔属性的类,例如以下示例:

public class Preprocessor
{
  public static final boolean FULLACCESS = false;
}
Run Code Online (Sandbox Code Playgroud)

然后我以下列方式使用它:

public ClassName getClassName()
{
    if(Preprocessor.FULLACCESS)
    {
        return this;
    }
    else
    {
        return this.DeepCopy();
    }
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,这很好,这解决了我的问题(上面的例子是微不足道的,但我确实在其他有用的情况下使用它).我的问题是,是否有办法将条件置于整个方法周围,以便在给定正确的"预处理器"变量的情况下方法本身不可用?例如,我希望能够使特定的构造函数仅适用于给定"完全访问"的包,如下所示:

public ClassName()
{
    // do things
}

if(FULLACCESS)
{
public ClassName(ClassName thing)
{
    // copy contents from thing to the object being created
}
}
Run Code Online (Sandbox Code Playgroud)

我再次意识到Java作为一种语言的局限性(或设计决策),并且我知道在大多数情况下这是不必要的.事实上,我已经考虑过简单地创建这些"额外"方法并将它们的整个代码放在条件中,同时如果条件不活动则抛出异常,但这是一个非常粗略的解决方案似乎没有用处当我向他们提供这些库时,我的程序员.

非常感谢您提前寻求帮助.

编辑:

为了补充这个问题,我试图这样做的原因是,通过使用异常作为解决方案,IDE实际上不会将方法显示为"可用".然而,再一次,它可能仅仅是我对Java的无知.

我想要这样做的原因主要是因为我可能有多个公共接口可用,例如,一个限制性,其中控制在方法中更严格,另一个允许直接改变属性.但是,我也希望能够从.class中主动删除部分代码,例如,在某些变体不可用的产品线开发方法中.

EDIT2:

此外,重要的是要注意我也将有条件地生成文档.因此,每个编译版本的软件包都有自己的文档,只包含实际可用的文档.

Mar*_*ger 6

好吧,你可以实现它.一句谨慎,但......

我只能想到有一次我认为这种方法是最好的方式,而事实证明我错了.更改类的公共接口的情况对我来说尤其像是一面红旗.当访问级别不足以调用方法时抛出异常可能更加代码友好.

但无论如何,当我以为我想要一个预处理器时,我所做的就是写一个.我创建了一个自定义注释来放置有条件可用的方法,抓住了一个Java解析器并编写了一个小程序,使用解析器来查找和删除具有注释的方法.然后将其(有条件地)添加到构建过程中.

因为它对我来说是无用的,所以我放弃了我的; 我从来没有见过其他人这样做并发表它; 所以据我所知你必须自己动手.


Lod*_*ijk 5

该答案部分基于您对问题和Mark的答案所发表的评论。

我建议您使用Java接口执行此操作,该接口仅公开您所需的API。当您需要限制较少的API合同时,可以扩展接口或创建现有接口的单独实现以获取所需的内容。

public interface A
{
    void f();
}
Run Code Online (Sandbox Code Playgroud)

A以上是您的常规API。现在,您需要一些特殊的额外方法来测试A,调试或操纵它或其他任何方法。

public interface B extends A
{
    void specialAccess();
}
Run Code Online (Sandbox Code Playgroud)

而且,Java现在支持接口的默认方法实现,这可能对您有用,这取决于您实现API的方式。他们采用以下形式...

public interface A
{
    List getList();

    // this is still only an interface, but you have a default impl. here
    default void add(Object o)
    {
        getList().add(o);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以在此处的Oracle页面上阅读有关默认方法的更多信息。

在您的API中,它的一般发行版可能会完全包含A和忽略B,并忽略所有提供特殊访问权限的实现;那么您可以B为您提到的API的特殊访问版本包含特殊实现。这将允许使用普通的旧Java对象,除了额外的接口和可能的额外实现之外,与代码没有什么不同。自定义部分将只在您的库包装中。如果要给某人一个“非特殊的”低访问权限版本,请给他们一个不包含B或不包含任何可能的jar BImplementation,可能是通过使用单独的构建脚本来完成的。

我将Netbeans用于我的Java工作,我希望它使用它自动生成的默认构建脚本。因此,如果我正在NetBeans中进行此操作,则可能会创建两个项目,一个用于基础API,一个用于特殊访问API,并且我将使特殊访问依赖于基础项目。那会给我留下两个罐子而不是一个,但我会满意的。如果两个罐子让我感到困扰,我将通过上面提到的额外步骤,为特殊访问版本制作一个构建脚本。


直接来自Java的一些示例

Swing有这种模式的示例。注意,GUI组件有一个void paint(Graphics g)。A Graphics为您提供了一组特定的功能。通常,这g实际上是一个Graphics2D,因此您可以根据需要将其视为此类。

void paint(Graphics g)
{
    Graphics2d g2d = Graphics2d.class.cast(g);
}
Run Code Online (Sandbox Code Playgroud)

另一个示例是Swing组件模型。如果使用a JList或a JComboBox在GUI中显示对象列表,则要在一段时间内更改列表,可能不使用其附带的默认模型。而是创建具有附加功能的新模型并将其注入。

JList list = new JList();
DefaultListModel model = new DefaultListModel();
list.setModel(model);
Run Code Online (Sandbox Code Playgroud)

现在,您的JList模型具有通常不明显的额外功能,包括轻松添加和删除项目的功能。

不仅以此方式添加了额外的功能,而且的原始作者ListModel甚至不需要知道该功能可能存在。