使用注释确保方法返回的值不会被丢弃

时间:2010-09-01 00:14:41

标签: java annotations return-value api-design

Java中的

String是不可变的。从广义上讲,以下片段是“错误的”。

String s = "hello world!";

s.toUpperCase(); // "wrong"!!

System.out.println(s); // still "hello world!"!!!

尽管这是“错误的”,代码编译并运行,也许是许多初学者的困惑,他们必须被告知错误是什么,或者通过查阅文档找出自己。

阅读文档是理解API的重要部分,但我想知道是否可以通过额外的编译时检查来补充这一点。特别是,我想知道是否可以使用Java的注释框架来强制执行某些方法返回的值不被忽略。然后,API设计者/库作者将在其方法中使用此注释来记录不应忽略哪些返回值。

一旦API补充了这个注释(或者可能是另一种机制),那么每当用户编写如上所述的代码时,它就不会编译(或者通过严厉的警告进行编译)。

所以可以这样做,你会怎样做这样的事情?


附录:动机

似乎很清楚,在一般情况下,Java 应该允许忽略方法的返回值。 List.add始终 true),System.setProperty(之前的值)等方法的返回值可能会在大多数情况下被安全地忽略。

但是,还有许多方法的返回值应该忽略 NOT 。这样做几乎总是程序员错误,或者不正确使用API​​。这些包括:

  • 关于不可变类型的方法(例如StringBigInteger等),它们返回操作的结果而不是改变它被调用的实例。
  • 返回值是其行为的关键部分并且不应忽略的方法,但人们有时会做(但InputStream.read(byte[])返回读取的字节数,应该 NOT 假设是数组的整个长度)

目前,我们可以编写忽略这些返回值的代码,并让它们在没有警告的情况下编译和运行。静态分析检查器/ bug查找程序/样式执行程序/等几乎可以肯定地将这些标记为可能的代码气味,但如果API本身可以通过注释强制执行,那么它似乎是合适的/理想的。

一个类几乎不可能确保它总是“正确”使用,但它可以做些什么来帮助指导客户正确使用(参见: Effective Java 2nd Edition,Item 58:Use检查可恢复条件的异常和编程错误的运行时异常项目62:记录每种方法抛出的所有异常)。有一个注释可以强制客户端不要忽略某些方法的返回值,并且编译器在编译时以错误或警告的形式强制执行它,这似乎符合这个想法。


附录2:Snippet

以下是初步尝试,简洁地说明了我想要实现的目标:

@interface Undiscardable { }
//attachable to methods to indicate that its
//return value must not be discarded

public class UndiscardableTest {
     public static @Undiscardable int f() {
             return 42;
     }

     public static void main(String[] args) {
             f(); // what do I have to do so this generates
                  // compilation warning/error?

             System.out.println(f()); // this one would be fine!
     }
}

上面的代码编译并运行正常(as seen on ideone.com)。我怎么能不这样做呢?如何将我想要的语义分配给@Undiscardable

7 个答案:

答案 0 :(得分:8)

我不确定可行性 - 特别是以便携式方式 - 但请查看来自Roman Numerals, in our Java GitHub code Adrian Kuhn)。他使用annotation processing AND Sun的javac私有API将罗马数字文字添加到Java ,方法是访问源代码以进行替换

也许您可以使用类似的方法:

  • 在源代码中找到对带注释的方法的调用
  • 检查结果是否已分配(不容易IMO)
    • 如果不是
    • 则生成编译器警告

不要错过Adrian的帖子中的以下资源:

  

您可能也喜欢

     

参考

相关问题

答案 1 :(得分:7)

您还可以查看jsr305,它定义了@CheckReturnValue注释。它与findbugs兼容,并在有人忘记处理返回值时生成警告。

Guavas Splitter使用它: http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/base/Splitter.java

我必须说我喜欢可以指导静态代码分析的注释。

答案 2 :(得分:2)

在一个坚果中:您希望有一个@Deprecated类似的注释,它可以帮助编译器/ IDE在调用方法时发出警告/错误而不指定其结果?如果不修改Java源代码和编译器,就无法实现这一点。必须注释特定方法,编译器必须知道它们。在不修改源和/或编译器的情况下,您可以最高限度地创建一种IDE插件/设置,用于识别案例并相应地生成错误/警告。


更新:你可以在它周围编写一个框架/插件,它会相应地检查被调用的方法和错误。您只希望在运行时提供注释。您可以使用@Retention (RetentionPolicy.RUNTIME)注释注释来执行此操作。然后,您可以使用Method#getAnnotation()来确定此注释是否可用。以下是这样一个框架如何完成这项工作的启动示例:

package com.example;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public class Test {

    public static void main(String[] args) throws Exception {
        if (Test.class.getMethod("f", new Class[0]).getAnnotation(Undiscardable.class) != null) {
            System.err.println("You should not discard the return value of f()!");
        } else {
            f();
        }

        System.out.println(f());
    }

    public static @Undiscardable int f() {
        return 42;
    }
}

@Retention(RetentionPolicy.RUNTIME)
@interface Undiscardable {}

然而,为了让编译器完成工作,你必须做更多的工作。

答案 3 :(得分:2)

在Android上,如果未使用返回值,则可以使用@CheckResult来显示警告。

public class ImmutableObject {

    public final int value;

    public ImmutableObject(int value) {
        this.value = value;
    }

    @CheckResult
    public ImmutableObject addOne() {
        return new ImmutableObject(value + 1);
    }
}

这将发出警告:

ImmutableObject obj = new ImmutableObj();
obj.addOne();  // Warning here
ImmutableObject obj2 = obj.addOne();  // No warning

如果使用RxJava,则还可以使用@CheckReturnValue

答案 4 :(得分:1)

您无需定义注释。您可以在调用方法时定义规则:

  1. 该方法具有void返回类型;
  2. 该方法的结果用作另一个方法调用的参数;或
  3. 将方法的结果分配给变量。
  4. 您可以实施一个处理器来强制执行此规则或实施一个强制执行此规则的Checkstyle。

答案 5 :(得分:1)

免责声明:实际上,我有同样的问题,还没有一个完整的解决方案。但是:

我知道如何以一种干净的方式完成这项工作,我想在这里发布,同时尝试完成它:

  1. 可以使用AspectJ在调用特定方法后调用代码。例如

    @AfterReturning(pointcut=“call(int Foo.m(int))”, returning=”x”)
    public void doSomething(int x){ ... }
    可用于。返回值x将传递给您的方法。

  2. 然后,您的方法可以查看返回值的引用计数。如果返回值为Garbadge Collected,它会被丢弃并且您可以发出警告,例如, http://java.dzone.com/articles/letting-garbage-collector-do-c

  3. 当然,我更倾向于对此进行注释和编译时支持,因为上述内容可能仅适用于测试环境,可能不适用于生产(由于其性能影响)。

    如果这可能有效,是否有任何意见?

答案 6 :(得分:0)

你有一个问题,问题是人们可能会错误地忘记使用方法的回报。通过使用注释,您告诉图书馆作者他们必须负责提醒他们的呼叫者不要丢弃某些方法的结果。

虽然这似乎是一个好主意,但我认为不是。我们是否想通过向用户发出有关其糟糕做法的通知来混淆代码?有很多产品可以查看代码,并告诉您何时在Lint,Sonar甚至JavaDocs等错误(或不合需要)的情况下做的更少。

如果您不同意图书馆作者所说的内容,我们现在应该使用@SuppressWarnings(" return-abandoned")。

虽然这可能有助于学习辅助,但我的观点更多的是关注点的分离,而不是帮助新手程序员。类中的代码(和注释)应该与类的功能相关,而不是规定何时以及如何使用它的方法的策略。