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

pol*_*nts 28 java annotations api-design return-value

String在Java中是不可变的.从广义上讲,以下片段是"错误的".

String s = "hello world!";

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

System.out.println(s); // still "hello world!"!!!
Run Code Online (Sandbox Code Playgroud)

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

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

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

那么这可以做到,你会怎么做这样的事情?


附录:动机

很明显,在一般情况下,Java 应该允许忽略方法的返回值.可以在大多数时间安全地忽略像List.add(always true),System.setProperty(previous value)这样的方法的返回值.

然而,也有很多的方法,其返回值应被忽略.这样做几乎总是程序员错误,或者不正确使用API​​.这包括以下内容:

  • 关于不可变类型(例如String,BigInteger等)的方法,它返回操作结果而不是改变它被调用的实例.
  • 方法,其返回值是其行为的重要组成部分,不应该被忽视,但人们有时会做呢(如InputStream.read(byte[])返回读取的字节数,这应该被假定为阵列的整个长度)

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

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


附录2:片段

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

@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!
     }
}
Run Code Online (Sandbox Code Playgroud)

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

Pas*_*ent 8

我不确定可行性 - 特别是以便携方式 - 但是看看Adrian Kuhn的Java(GitHub代码)中的Roman Numerals.他使用注释处理 Sun的私有API 通过访问源代码来将罗马数字文字添加到Java中以进行一些替换. javac

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

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

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

您可能还喜欢

参考

相关问题

  • 1.扩展http://download.oracle.com/javase/6/docs/jdk/api/javac/tree/com/sun/source/util/TreePathScanner.html 2.覆盖visitAssignment,visitMethodInvocation,或许还有其他人 (2认同)

jon*_*ejj 8

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

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

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


Bal*_*usC 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 {}
Run Code Online (Sandbox Code Playgroud)

尽管如此,要让编译器完成这项工作,您还必须做更多的工作。