Java 在编译时检查传递给方法的字符串参数是否具有@deprecated注释

Java 在编译时检查传递给方法的字符串参数是否具有@deprecated注释,java,validation,compiler-construction,annotations,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

我想验证传递给方法的字符串是否已弃用。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
    }
}
我能找到的最近的标记是。

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

如果使用
@Deprecated
注释,您将在使用Deprecated字段的行中收到Deprecated警告:

@Deprecated
private static final String OLD_PATH = "old_path";
private static final String NEW_PATH = "new_path";
您也可以保留JavaDoc
@deprecated
标记,但只有在提供一些解释的情况下,它才有用。javadoc标记必须位于
/***/


但是,如果要在运行时在
migrateProperty()
方法中检查传递的字符串是否来自不推荐使用的变量,这是不可能的。方法调用得到的是对堆上字符串的引用。弃用仅指字段,该字段可能只能在调用方法之前进行检查。

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

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


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

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

以你为例,我很容易打电话给你

new MyRepo().loadProperty("old_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
    }
}
如果需要将此模式应用于多个类,当然可以使其更加严格。

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


如果你能写一个,我会对使用这段代码非常感兴趣:)

我的方法是把它变成编译器已经擅长的东西:类型检查

基于您的示例中常数的使用,我将假设您有一组已知的潜在值,这表示
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
    }
}
如果这不符合您的需要,请添加有关您的使用场景以及您试图解决的潜在问题的更多信息



如果您真的打算通过注释来实现这一点,那么似乎有一种方法。Java6在
javac
中内置了注释处理API,这些API似乎是编译器的插件。他们可以做你想做的事,而且还可以做很多,但至少乍一看,他们似乎相当深奥。这看起来是一个很好的介绍:

您的注释将字段
OLD\u PATH
标记为已弃用,而不是字符串
“OLD\u 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
    }
    // ...
}
@已弃用
私有静态最终字符串OLD\u PATH=“OLD\u PATH”;
私有静态最终字符串NEW\u PATH=“NEW\u PATH”;
公共负载(节点){
migrateProperty(节点,
getClass().getDeclaredField(“旧路径”),
getClass().getDeclaredField(“新路径”);
// ...
}
//我想验证字符串oldPath是否具有@Deprecated注释
public void migrateProperty(节点节点、字段oldPath、字段newPath){
if(oldPath.getAnnotation(弃用的.class)==null){
//…无效
}
// ...
}

在这种情况下,您实际上传递的是字段,而不是它的值。

在编译时根本不可能这样做

首先,@Distributed注释可以引用字符串字段,但无论如何都不会附加在字符串值中

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

由于第1点,注释处理无法工作。由于第2点,所有其他方案仅在运行时有效


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

编译器在生成的类文件中确实包含了基于JavaDoc的@deprecated标记。否则,在针对提供的第三方库(甚至是标准API)进行编译时,不推荐使用警告将不可用。我已将示例代码更改为使用@Deprecated。我没有收到任何警告,除了通过变量敲打。不过,我不想看到任何警告,我想验证@Deprecated注释是否存在于f上