我正在尝试实现与C#预处理器类似的功能。我知道Java没有相同的预处理器功能,并且我知道有很多方法可以使用Factory等设计模式来实现类似的结果。但是,我仍然有兴趣找到这个问题的解决方案。
目前,我所做的是创建一个包含多个静态最终布尔属性的类,例如以下示例:
public class Preprocessor
{
public static final boolean FULLACCESS = false;
}
然后我以下列方式使用它:
public ClassName getClassName()
{
if(Preprocessor.FULLACCESS)
{
return this;
}
else
{
return this.DeepCopy();
}
}
到目前为止,这很好,这解决了我的问题(上面的例子是微不足道的,但我确实在其他有用的情况下使用它)。我的问题是,是否有办法将条件置于整个方法周围,以便在给定正确的"预处理器"之前,方法本身将不可用。变量?例如,我希望能够使特定的构造函数仅适用于给定"完全访问"的包,如下所示:
public ClassName()
{
// do things
}
if(FULLACCESS)
{
public ClassName(ClassName thing)
{
// copy contents from thing to the object being created
}
}
同样,我知道Java作为一种语言的局限性(或设计决策),并且我知道在大多数情况下这是不必要的。事实上,我考虑过简单地创建这些" extra"方法并将它们的整个代码放在条件中,同时如果条件不活动则抛出异常,但这是一个非常粗糙的解决方案,当我向他们提供这些库时,对我的程序员似乎没有帮助。
非常感谢您提前寻求帮助。
编辑:
为了补充这个问题,我试图这样做的原因是,通过使用异常作为解决方案,IDE将显示方法为"可用"当它们实际上不是。然而,再一次,它可能仅仅是我对Java的无知。
我想要这样做的原因主要是因为我可能有多个公共接口可用,例如,一个限制性,其中控制在方法中更严格,另一个允许直接改变属性。但是,我也希望能够从.class中主动删除部分代码,例如,在某些变体不可用的产品线开发方法中。
EDIT2:
此外,重要的是要注意我也将有条件地生成文档。因此,软件包的每个编译版本都有自己的文档,只包含实际可用的文档。
答案 0 :(得分:6)
嗯,你可以实现它。提醒一下,不过......
我只能想到有一次我认为这种方法是最好的方法,而事实证明我错了。更改班级公共界面的情况对我来说尤其像是一面红旗。当访问级别不足以调用该方法时抛出异常可能对代码更友好。
但无论如何,当我以为我想要一个预处理器时,我所做的就是写一个。我创建了一个自定义注释来放置有条件可用的方法,抓住了一个Java解析器并编写了一个小程序,使用解析器来查找和删除带有注释的方法。然后将其(有条件地)添加到构建过程中。
因为事实证明这对我没用,我放弃了我的;我从来没有见过其他人这样做并发表它;所以据我所知,你必须自己动手。
答案 1 :(得分:4)
这个答案部分基于您在问题上留下的评论以及Mark的回答。
我建议你使用Java接口来实现这一点,这些接口只公开你想要的API。当您需要限制较少的API合约时,请扩展接口或创建现有接口的单独实现以获得所需的内容。
public interface A
{
void f();
}
上面的 A
是您的常规API。现在你想要一些特殊的额外方法来测试A
或调试它或操纵它或其他什么......
public interface B extends A
{
void specialAccess();
}
此外,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);
}
}
您可以在Oracle's page about it here上阅读有关默认方法的更多信息。
在您的API中,您的一般发布可能包括A
并完全省略B
,并省略任何提供特殊访问权限的实现;然后,您可以包含B
以及您提到的API的特殊访问版本的特殊实现。这将允许普通的旧Java对象,除了额外的接口之外没有什么不同的代码,也许是它的额外实现。自定义部件将位于库的包装中。如果你想交给某人一个非特殊的"低访问版本,将它们放在一个不包含B
的jar中,并且不包含任何可能的BImplementation
,可能有一个单独的构建脚本。
我使用Netbeans进行Java工作,我喜欢让它使用它自动生成的默认构建脚本。因此,如果我这样做并且我在Netbeans中这样做,我可能会创建两个项目,一个用于基本API,一个用于特殊访问API,我将使特殊访问一个依赖于基础项目。那会让我有两个罐子,而不是一个,但我会好起来的;如果两个罐子让我感到困扰,我会通过上面提到的为特殊访问版本制作构建脚本的额外步骤。
直接来自Java的一些例子
Swing有这种模式的例子。请注意,GUI组件具有void paint(Graphics g)
。 Graphics
为您提供了一组特定的功能。通常,g
实际上是Graphics2D
,因此如果您愿意,可以将其视为此类。
void paint(Graphics g)
{
Graphics2d g2d = Graphics2d.class.cast(g);
}
另一个例子是使用Swing组件模型。如果使用JList
或JComboBox
来显示GUI中的对象列表,如果要随时更改该列表,则可能不会使用它附带的默认模型。相反,您创建一个具有附加功能的新模型并将其注入。
JList list = new JList();
DefaultListModel model = new DefaultListModel();
list.setModel(model);
现在,您的JList模型具有通常不明显的额外功能,包括轻松添加和删除项目的功能。
不仅以这种方式添加了额外的功能,而ListModel
的原始作者甚至不需要知道此功能可能存在。
答案 2 :(得分:0)
使用Gradle,您可以管理您的源,我认为不再需要预处理器宏。目前在src
目录中,您拥有main/java
所有来源,但如果您需要特定方法,例如debug
和release
构建要执行/不执行特定事项,然后在debug/java
中创建release/java
和src
并将YourClass
放在那里。请注意,通过这样做,您必须YourClass
和debug/java
中release/java
,而main/java
中不能{。}}。
答案 3 :(得分:0)
Java中达到这一目标的唯一方法是使用预处理器,例如PostgresJDBC团队使用java comment preprocessor进行此类操作,这里是他们Driver.java
的示例 //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
@Override
public java.util.logging.Logger getParentLogger() {
return PARENT_LOGGER;
}
//#endif