如何告诉 Android Studio 方法不返回

Posted

技术标签:

【中文标题】如何告诉 Android Studio 方法不返回【英文标题】:How to tell Android studio that a method does not return 【发布时间】:2020-04-15 23:14:27 【问题描述】:

我有一个永远不会返回的 android 错误处理程序(它会记录一条消息并引发错误异常)。如果我从通常返回值的方法调用错误处理程序,Android Studio lint 检查器会报告错误,因为没有返回值。有没有办法告诉 Android Studio 我的错误处理程序没有返回,或者在调用它之后代码中的点实际上是无法访问的。

当然,我可以放入一个不必要的 return 语句,返回一个正确类型的虚拟值,但这很不雅,并且会用无法访问的语句弄乱我的应用程序。

我找不到要禁用的代码检查以防止错误,但即使有一个要禁用,它也会停止报告真正缺少的返回语句。

重复一遍,这不是 Java 语法问题。人们说过,Java 方法必须返回声明类型的值。这是 (a) 不相关 (b) 不正确。 正确的说法是,Java 方法如果返回,必须返回声明类型的值。这段代码

public long getUnsignedLong(String columnName)
    throws NumberFormatException, NoColumnException

    String s = getString(columnName, "getUnsignedLong");
    if ((s != null) && s.matches("^[0-9]+$")) 
        return Long.parseLong(s);
    
    else
    
        throw(new NumberFormatException("Bad number " + s));
    

是完全有效的 Java,AS 不会抱怨它。事实上,如果我像这样插入一个不必要的return

public long getUnsignedLong(String columnName)
    throws NumberFormatException, NoColumnException

    String s = getString(columnName, "getUnsignedLong");
    if ((s != null) && s.matches("^[0-9]+$")) 
        return Long.parseLong(s);
    
    else
    
        throw(new NumberFormatException("Bad number " + s));
    
    return 0;

AS 抱怨它无法访问。

我抛出异常的问题是,如果它真的发生了,我的应用程序的用户看到的是一个弹出窗口,提示应用程序已停止并询问用户是否要禁用它。这对用户不是很有帮助,当用户向我报告它已经发生时,对我也不是很有帮助。因此,我没有抛出异常,而是调用了如下所示的致命错误处理程序:-

// Always invoked with fatal = true
// Report a fatal error.
// We send a message to the log file (if logging is enabled).
// If this thread is the UI thread, we display a Toast:
// otherwise we show a notification.
// Then we throw an Error exception which will cause Android to
// terminate the thread and display a (not so helpful) message.
public MyLog(Context context, boolean fatal, String small, String big) 
    new Notifier(context, small, big);
    new MyLog(context, big, false);
    throw(new Error());

是的,我知道参数fatal 没有被引用,但是它的存在安排了我的错误处理程序的这个特殊重载被调用,你可以看到它抛出了一个异常并且没有返回。

我的实际问题是,如果我通过调用不返回的致命错误处理程序替换 getUnsignedLong 中的 throw,则 AS 在 getUnsignedLong 末尾抱怨它返回时没有值.嗯,这是错误:这一点和以前一样不可触及。我尝试在MyLog 前面放置一个合同,说它总是失败,但这无济于事,并且在 AS 错误报告中按右箭头并没有提供任何抑制它的方法。我可以输入一个虚拟的return 语句或一个虚拟的throw,但其中任何一个实际上都是无法访问的代码,我认为这是不优雅和不必要的。

所以我的问题与最初提出的问题一样:我如何告诉 Android Studio 一个方法没有返回?

【问题讨论】:

这与 AS 无关,它是 Java 语法,在 Java 中不是 void 方法总是必须返回一个值。 为什么不返回null? 您可以禁用链接检查器,也可以使用 void 方法 发布您的代码。抛出异常的分支不需要return语句,但从你所说的看起来你有一个也不抛出异常的分支? 【参考方案1】:

您的问题似乎是 Java 问题。

在 Java 中,非 void 方法必须返回它应该返回的类型。你的情况是一样的。

所以简单的解决方案是返回一个虚拟值。这是为了编译器。

最好的(更难的)解决方案是避免这种结构。如果一个方法正常返回一个值,该方法就会有返回值,如果出现错误,就会发生异常。错误处理程序应该处理错误,这意味着它不是默认行为。

另外,你可能想看看here: unreachable-code-error-vs-dead-code-warning-in-java

如果您担心单元测试覆盖率。当你进行单元测试时,总有一些你无法触及的代码部分。这就是为什么我们几乎从不想要 100 的覆盖率的原因之一

【讨论】:

这不是语法错误:这是流分析中的错误。 AS中的java编译器可以检测到错误处理程序总是抛出异常并且不返回(如果我在throw之后放入return语句,它会抱怨),但它不会将该事实传播回流程在调用错误处理程序的方法中进行分析,因此它(错误地)认为在调用错误处理程序之后应该有一个返回,尽管此时的返回实际上是无法到达的。【参考方案2】:

方法基本上是您希望以不同方式访问的代码(在一行中调用一个方法比代码超过 50 行等更容易)。当您创建方法时,您可以调用它:

“void”(从不返回值),

“字符串”(返回一组字符/字母),

"int"(返回其范围内的数字。Integer Types),

"boolean"(返回 2 类型的值,真或假)。

还有更多...

在这些方法中,你可以做任何你想做的事情,但要确保它们最终返回初始化中指定的值类型。

例子:

int y = 2;

boolean booleanMethod()
  y = 6;
  return true; //or false, doesn't really matter in this case.


boolean trueOrFalse()
  if (y == 2) return true;
  else return false;


//or

void method(int nr)
  nr = 10;

这些只是 Java* 中方法的一些基本示例,因为您的问题是语法问题,而不是真正的 AS 问题。

【讨论】:

【参考方案3】:

AS 抱怨它无法访问。

Android Studio 是正确的。 Android Studio 正在正确实现 Java 语言规范中的可达性规则。

这是一个 Java 语义问题。 (这不是语法问题,但不要分叉。)

让我们创建一个简单但完整的示例来说明为什么Android Studio 是正确的:

public class Test 
    public int method1() throws Exception 
        int result;
        method2();
        return result;
    
    
    public boolean method2() throws Exception 
        throw new Exception("bad stuff");
    


$ javac Test.java 
  Test.java:5: error: variable result might not have been initialized
              return result;
                     ^
  1 error

(我手头没有 Android Studio 的副本,但这会产生与 javac 类似的编译错误。试试吧。)

为什么这是一个错误?毕竟,根据您的推理,return 语句应该是无法访问的。

这就是问题所在。 JLS 不是这么说的。事实上,JLS 14.21 说了以下内容。 (这些陈述是摘录,为了清楚起见,我添加了数字。“iff”是 JLS 用于“当且仅当”的缩写。)

    “作为构造函数、方法、实例初始化器或静态初始化器主体的块是可访问的。”

    “非空块中的第一个语句不是 switch 块,如果该块是可访问的,则该块是可访问的。”

    “一个局部变量声明语句只要可达就可以正常完成。”

    “如果 S 之前的语句可以正常完成,则非空块中不是 switch 块的所有其他语句 S 都是可访问的。”

    “一个表达式语句只要可达,就可以正常完成。”

考虑method1的正文。

通过 #1 - 可以访问该块 通过#2 - 可以访问result 的声明。 通过#3 - 声明可以正常完成 通过#4 - 可以调用method2() 通过#5 - 调用可以正常返回 通过#4 - 可以访问return 语句。

但同样清楚的是,如果我们到达return 语句,那么result 肯定不会被初始化。 (这显然是真的。JLS 16 证明了这一点。)


好吧,那他们为什么要这样指定 Java?

一般情况下,method1method2 可以在不同的编译单元中;例如案例AB。这意味着这些方法可以在不同的时间编译,并且只在运行时组合在一起。现在,如果编译器需要分析B.method2 的主体以确定A.method1 中的return 是否可访问,那么考虑以下情况会发生什么:

B.method2的代码被修改,B在编译A后重新编译,或者 加载了B 的子类C,其中C.method2 正常返回。

简而言之,如果我们在分析method 中的可达性时需要考虑method2 内部的流程,我们无法在编译时得出答案。


结论:

    JLS 清楚地表明/意味着(我的)示例程序是错误的。 这不是规范错误。 如果 Android Studio(或 javac没有调用错误的示例,那么它不会是 Java 的有效实现。

【讨论】:

以上是关于如何告诉 Android Studio 方法不返回的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android Studio 中创建动态 ListView

我该如何使用此Webview Bar? - Android Studio

Android Studio 中的 EditorConfig

如何在 Android Studio 中查看方法信息

如何在Android Studio上将句子中的单词斜体?

android studio中的cursorLoader