如果传递给method的字符串参数有@deprecated注释,则在编译时检查

时间:2011-06-16 14:51:53

标签: java validation compiler-construction annotations

我想验证传递给方法的字符串是否已弃用。 e.g:

public class MyRepo
    @Deprecated
    private static final String OLD_PATH = "old_path";
    private static final String NEW_PATH = "new_path";

    //...

    public load(Node node){
        migrateProperty(node, OLD_PATH , NEW_PATH );

        //load the properties
        loadProperty(node, NEW_PATH);
    }

    //I want to validate that the String oldPath has the @Deprecated annotation
    public void migrateProperty(Node node, String oldPath, String newPath) {
        if(node.hasProperty(oldPath)){
            Property property = node.getProperty(oldPath);
            node.setProperty(newPath, (Value) property);
            property.remove();
        }
    }

    //I want to validate that the String path does not have the @Deprecated annotation
    public void loadProperty(Node node, String path) {
        //load the property from the node
    }
}

我能找到的最近的是validating annotations on the parameters themselves

7 个答案:

答案 0 :(得分:1)

您的注释将字段OLD_PATH标记为已弃用,而不是字符串"old_path"。在调用migrateProperty时,您传递字符串,而不是字段。因此,该方法不知道值来自的字段,也无法检查它是否有注释。

使用注释,您可以陈述Java元素,例如类,字段,变量和方法。你不能注释对象,比如字符串。

您链接的文章讨论了注释形式参数。同样,它是带注释的参数,而不是参数(传递的值)。如果将@Something放入方法参数,则此参数将始终独立于此方法的调用者传递的值进行注释。

你可以做什么 - 但我不确定这是不是你想要的 - 如下:

@Deprecated
private static final String OLD_PATH = "old_path";
private static final String NEW_PATH = "new_path";

public load(Node node){
    migrateProperty(node,
        getClass().getDeclaredField("OLD_PATH"),
        getClass().getDeclaredField("NEW_PATH") );
    // ...
}

//I want to validate that the String oldPath has the @Deprecated annotation
public void migrateProperty(Node node, Field<String> oldPath, Field<String> newPath) {
    if ( oldPath.getAnnotation(Deprecated.class) == null ) {
       // ... invalid
    }
    // ...
}

在这种情况下,你真的通过了这个领域,而不是它的价值。

答案 1 :(得分:0)

首先,您的“@deprecated”标记只是一个JavaDoc标记,而不是注释,因此它与编译器无关。

如果您使用@Deprecated注释,您将获得使用已弃用字段的行的弃用警告:

@Deprecated
private static final String OLD_PATH = "old_path";
private static final String NEW_PATH = "new_path";

您也可以保留JavaDoc @deprecated标记,但只有在您提供一些解释时它才有用。当然,javadoc标签必须位于/** ... */内。


但是,如果要在运行时检查字符串传递的migrateProperty()方法中的运行时来自已弃用的变量,则这是不可能的。您通过方法调用获得的是对堆上的String的引用。弃用仅引用该字段,可能只能在调用方法之前检查该字段。

答案 2 :(得分:0)

“旧”(Java 5之前,基于JavaDoc)不推荐使用的注释存储在已编译的类文件中,但遗憾的是无法通过反射访问。

如果您可以选择使用“真实”注释(@ java.lang.Deprecated),您当然可以使用反射来获取类的所有声明字段,检查它们是否是静态字符串@Deprecated注释并将它们与传递的方法参数进行比较。

然而,这听起来相当丑陋,我鼓励你找到一种不同的方法来检查不需要的参数。

答案 3 :(得分:0)

我不知道你的用例到底是什么,但我认为你不能用@Deprecated做你想做的事。当您将某些内容标记为已弃用时,您将标记字段,方法或类而不是值。您在loadProperty中获得的所有权限都是值。

所以举个例子,我可以很轻松地打电话给

new MyRepo().loadProperty("old_path");

根本没有引用OLD_PATH或NEW_PATH。解决方案很简单,您需要在方法中明确检查匹配。 (isDeprecated方法添加)。如果您愿意,可以留下@Deprecated注释作为指示。

public class MyRepo {
    @Deprecated
    private static final String OLD_PATH = "old_path";
    private static final String NEW_PATH = "new_path";

    private boolean isDeprecated(String path) {
        return OLD_PATH.equals("old_path");
    }

    //...

    public load(Node node){
        migrateProperty(node, OLD_PATH , NEW_PATH );

        //load the properties
        loadProperty(node, NEW_PATH);
    }

    //I want to validate that the String oldPath has the @Deprecated annotation
    public void migrateProperty(Node node, String oldPath, String newPath) {
        if (!isDeprecated(oldPath)) {
            throw new Exception(oldPath + " is not deprecated");
        }

        if(node.hasProperty(oldPath)){
            Property property = node.getProperty(oldPath);
            node.setProperty(newPath, (Value) property);
            property.remove();
        }
    }

    //I want to validate that the String path does not have the @Deprecated annotation
    public void loadProperty(Node node, String path) {
        if (isDeprecated(path)) {
            throw new Exception(path + " is deprecated, please use " + NEW_PATH);
        }

        //load the property from the node
    }
}

如果需要将此模式应用于多个类,您当然可以使其更加严谨。

答案 4 :(得分:0)

检查“.class”文件是否适合您的“编译”时间要求? FindBug允许对.class文件进行多次检查。您可以编写自定义插件来检查字段,方法和参数(以及许多其他内容)。这是一个旧tutorial

如果你设法写一个,我将非常有兴趣使用该代码:)

答案 5 :(得分:0)

我的方法是将其改为编译器已经擅长的东西:类型检查。

根据您示例中常量的使用,我将假设您有一组已知的潜在值,这表示enum s。

public class MyRepo
    private enum Preferred {
        PATH("new_path"),
        OTHER_THING("bar");

        private String value;

        Preferred(String value) {
            this.value = value;
        }

        @Override
        public String toString() {
             return value;
        }
    }

    private enum Legacy {
        PATH("old_path"),
        OTHER_THING("foo");

        private String value;

        Legacy(String value) {
            this.value = value;
        }

        @Override
        public String toString() {
             return value;
        }
    }

    public load(Node node){
        migrateProperty(node, Legacy.PATH, Preferred.PATH);

        //load the properties
        loadProperty(node, Preferred.PATH);
    }

    public void migrateProperty(Node node, Legacy oldBusted, Preferred newHotness) {
        if (node.hasProperty(oldBusted)) {
            Property property = node.getProperty(oldBusted);
            node.setProperty(newHotness, (Value) property);
            property.remove();
        }
    }

    public void loadProperty(Node node, Preferred path) {
        //load the property from the node
    }
}

如果这不符合您的需求,请添加一些有关您的使用方案的信息以及您尝试解决的潜在问题。


如果您真的开始通过注释完成此操作,那么 就会出现。 Java 6具有内置于javac的注释处理API,它们似乎是编译器的有效插件。他们可以做你想要的事情加上更多,但它们看起来相当深奥,至少在第一眼看上去。这看起来很好:http://today.java.net/pub/a/today/2008/04/10/source-code-analysis-using-java-6-compiler-apis.html

答案 6 :(得分:0)

在编译时根本无法做到这一点。

首先,@ Distreciated注释可以引用一个String字段,但不会以任何方式附加字符串值。

其次,即使您以某种方式使用注释标记字符串值,Java类型系统中的任何内容都不允许您声明只能传递具有特定注释的值 - 注释信息在编译时甚至不可用时间。

由于第1点,注释处理将不起作用。由于第2点,所有其他方案仅在运行时工作。

要实现编译时检查,最自然的方法是创建一个包含字符串的所有有效值的枚举。