Java注解改变null值

问题描述 投票:0回答:1

我想编写一个注释,可以将参数值更改为默认值(如果为空)。 例如:

static void myMethod(@NullToDefault String param) {
    System.out.println(param);
}

public static void main(String[] args) {
    myMethod("foo"); //prints "foo"
    myMethod(null);  //prints "default"
}

我分别有一个注释和一个更改空值的方法。但我不知道如何强制注释默认调用此方法。我应该做什么来完成这个?

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface NullToDefault {
}

public static void inject(Object instance) { //how to call this automatically?
    Field[] fields = instance.getClass().getDeclaredFields();
    for (Field field : fields) {
        if (field.isAnnotationPresent(NullToDefault.class)) {
            field.setAccessible(true);
            try {
                if (field.get(instance) == null) {
                    field.set(instance, "default");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
java annotations java-annotations
1个回答
0
投票

这不是“直接”可能的。但也有窍门。 注释处理器

注释处理器 (AP) 有一个 API,限制它们制作

源文件,它们无法编辑现有文件。因此,“编写一个注释处理器来注入用某些默认值替换 null 值的代码”是不可能的。

但是,还有一些接近的替代方案。

您可以设置注释系统,以便用户(即使用您的注释的程序员)用 java 编写“模板”,并且您的 AP 可以看到它并生成真正的源文件。 AP 只能“看到”签名(方法的名称、返回类型、参数类型、参数名称和 throws 子句,以及字段的名称+类型和内部类型列表,

但不是

字段的初始化表达式,或方法声明的主体)。 这确实允许你做这样的事情:

// This is a file you write @SomeAnno public class FooDefinition { private final String name; public static void greet(Foo self) { System.out.println("Hello, " + self.getName()); } }

// and this is what your AP can generate from that
public class Foo {
  private final String name;

  public Foo(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void greet() {
    FooDefinition.greet(this);
  }
}
这是一个假设的 AP,添加了 getter 和构造函数。它之所以“有效”,是因为您需要从 FooDefinition 中了解生成 Foo 的所有信息,即 
final String name;

存在,并且

static void greet(Foo self)
存在。您不需要知道该greet方法的内容(因为您不能)。
这并不是一种“很好”的体验:在您进行完整的编译运行之前,Foo 并不存在,并且对 

FooDefinition

的任何修改都不会影响

Foo
,直到您进行另一次完整的构建运行。在 IDE 中进行快速更改并立即在源代码中反映出来,您不会从中受益。我不确定这对您的需求是否有用 - 最多,您可以对您的类进行包装克隆,其中您生成的代码中每个方法的主体将任何空参数替换为其默认值,然后调用您的代码.
代理商

代理是调试辅助工具,可以“插入”JVM 的核心功能,并且可以动态修改内容。特别是,他们可以见证原始字节码(以

byte[]

形式,如

.class
文件内容的直接转储),并且可以根据需要对其进行修改。您可以在启动时注册它们(
java -javaagent:/path/to/some.jar
) - 您可以制作一个可以看到注释并干预字节码以注入默认替换器的代码。
这有点奇怪,因为您必须有一个运行时组件(该代理,必须使用 

-javaagent:

参数指定。

这绝对是可能的,并且本质上是“实时”的,即在 IDE 中,您只需进行编辑、保存,然后即可。 eclipse 的热代码替换在这里可以正常工作,因为您的代理仍然在那里,并且动态注入初始化代码,用默认值替换空值。

然而,这

真的很困难

——经验丰富的java程序员可能仍然需要整整一周的时间来构建这样一个工具。您需要研究 Java 字节码库,例如 ObjectWeb 的 ASM 或 BCEL,因为您需要首先读取字节码,并且必须生成 bytecode,而不是 java 代码。 龙目岛

Lombok 经常作为“注释处理器”出售(有时甚至由我们出售 - 我是 lombok 的核心贡献者)。但事实并非如此:它是一个编译器插件,有时只是通过注释处理器路径加载。它“精确”地实现了您所谈论的内容,并且“建立默认机制”是一个非常常见的请求。您可能应该在 ProjectLombok github 主页上搜索有关此问题和 wiki 的问题,因为已经有很多关于这个想法的文字了。

鉴于 lombok 不是 AP,如果您想要这个,您可以分叉 lombok,然后编写并使用它,或者,如果您找到一种非常非常巧妙的方法来执行此操作,请发送 PR。我们(lombok 作者)还没有这样做的主要原因是注释处理器对您可以传入的参数的内容极为有限,因此我们不确定如何表达默认值。 只是...代码

它不会是自动的,但你当然可以总是简单地做这样的事情:

// code you actually write static void myMethod(@NullToDefault String param) { param = applyDefaults("param", param); System.out.println(param); }

即您每次都必须手动编写

applyDefaults
 行,但导致的代码至少可以读出该注释。它无法读出 
param

(名字),因此,我们通过它。

    

© www.soinside.com 2019 - 2024. All rights reserved.