具有@NotNull/@Nullable支持的Actionscript 3静态分析器

4
我希望使用静态分析来避免“TypeError:Error#1009:无法访问空对象引用的属性或方法”。
据我所知,Java有一些使用@NotNull / @Nullable的静态分析器。程序员通过为每个成员/函数/参数指定这些注释来明确表达他的意图,以便工具可以验证是否所有内容都是正确的,并且每个@Nullable-> @NotNull转换都是带有null检查的。对我来说,只指定@Nullable会更舒适,因此其他所有内容都可以像@NotNull一样处理,但这不是必需的。
我想知道是否有免费的类似Actionscript 3的工具?
我有FlashBuilder 4.5许可证,但除此之外,我宁愿不购买任何东西。我听说IntelliJ IDEA可能有这样的东西,但它仅适用于Ultimate Edition中的Actionscript,而该版本不免费:(
所以问题是:
1. 是否有适用于Actionscript 3的这种静态分析器? 2. 如果不是免费的,它会花费多少钱? 3. 除了“检查所有内容并将您的代码转换成混乱”之外,是否有其他解决方案来防止1009错误?
更新
没有给出完整的答案:(但是weltraumpirat的回答到目前为止是最好的。我考虑为可为空的函数使用命名约定:Try*,Find*,*OrNull,*IfAny等。名称变得更长,但可靠性更重要。谢谢weltraumpirat!赏金是你的,但我不会将答案标记为“已接受”,因为它不完整,我希望有一段时间会有人回答带有所需静态分析器链接的问题:)
5个回答

6
据我所知,没有针对ActionScript的静态分析工具可以真正预防空引用错误 - AS是一种动态语言,而它成为一种好的语言的很多特性都发生在编译器(或静态分析工具)无法验证的范围之外。
但您可以并且应该以一种能够预防空引用的方式设计您的代码,并允许有意义的错误消息帮助您更快地找到错误。
这样想:如果一个变量实例在某个时刻不应为空,那么程序是否应该继续运行? 在大多数情况下,答案显然是:不应该!
例如,常见的错误是在程序的每个可行点添加空检查,如下所示:
function myMethod( something:* ) : void {
    if ( something != null) {
       doStuff();
    }
}

这样做的结果是,程序不会抛出错误(这将使您能够找到崩溃发生的方法名称和行号,以便跟踪问题),而是继续运行,但什么也不会发生。现在当您执行手动测试时,您会看到:什么也没有。并且没有任何迹象表明出了什么问题。

处理空引用的更好方法是:

  • Make sure none of your methods return null. Ever.

    This can be achieved by returning default values, such as:

    function getPropertyValue() : String { 
        return _property != null ? _property : "";
    }
    

    And of course, the default value could be something meaningful, such as "Property did not have a value.", so that in your UI, you'd notice the problem right away, without compromising the rest of the program. (Note that this does not only work for getters, but for any method that returns a value - I simply chose an obvious example.)

    If you really can't get around returning null for some reason, make sure your method name reflects that, so when you use it, you know to expect possible null values:

     function getPropertyValueOrNull() : String {
         return _property;
     }
    
  • Instead of useless null checks, create validation methods at critical points in your application, and throw meaningful errors to point to the cause of the problem:

    function tryExecuteImportantFunction() : void {
        try {
             validate();
             executeImportantFunction();
        } catch ( e:Error ) {
             trace( "Validation failed:" + e.message );
        }
    }
    
    function validate() : void {
        if (_requiredProperty1 == null) 
            throw new Error ( "_requiredProperty1 was null, but should always contain a string value.");
        if (_requiredProperty2 == null) 
            throw new Error ( "_requiredProperty2 was null, but should always contain a dictionary.");
    }
    

    Meaningful errors should be caught and forwarded to the log or a debugging console, so that you always know where to look for the cause, and get an idea what you have to do to fix it.


对于未初始化的成员变量,这就是验证方法发挥作用的地方。如果您使用try/catch块,您将能够处理缺陷,并且您可以有一个中心位置来检查运行特定任务所需的所有内容 - 而不是在类中分散进行空值检查。 - weltraumpirat
我还是不明白。这个项目是一个游戏,所以没有重要/不重要的方法和类。而且问题肯定不在输入数据(网络)方面,所以我看不到任何可以进行验证的地方。这些错误只是小的本地错误(离错别字不远),这是人类的自然现象。同样,try/catch块 - 我找不到它们比其他地方更合理的地方。此外,所有这些验证和检查都在运行时进行,因此会减慢执行速度。 - Ilya Denisov
你说:“AS是一种动态语言,而且很多使它成为好语言的因素发生在编译器无法验证的范围内。”但是对于一些特定的小规模情况,我不使用动态类而是使用对象。并且我不想对它们进行检查。但是通过严格模式和非动态类,我看不出那些我提到的静态检查不可能实现的理由。 - Ilya Denisov
“验证”在这里的意思是“查看所有部分是否齐全”,不仅限于用户输入。好的放置位置包括:在init方法之后,或在render方法、游戏循环或事件触发操作的顶部。每个验证只包含几个简单的if语句,用于检查null - 即使有很多这样的语句,也不会导致明显的减速效果。如果没有错误,try/catch块将不会产生任何成本 - 在这种情况下,它们不应该是您的首要关注点。 - weltraumpirat
你的回答是目前为止最好的。即使它只是答案的一部分 :) 奖励归你所有。我考虑为可空函数使用命名约定:Try,FindOrNull,IfAny等。名称变得更长了,但可靠性更重要。 - Ilya Denisov
显示剩余2条评论

0

我完全同意weltraumpirat关于编程的观点,即尽可能避免使用null。然而,在我的经验中,这并不总是可行的。在最近的一个项目中,我们检索和生成了大量动态数据,这些数据的类型和形状都在变化。

网络超时、回调退出以及其他任何意外情况都会导致null停止应用程序。我们知道null错误并不重要,并且在许多情况下我们都预期它会出现。

这就需要为游戏中的所有错误情况提供动态错误处理。try catch语句要么太慢,要么需要大量维护。Flash Player提供了一种方法来处理这个问题,虽然它可能不是每个问题集的最合适解决方案。这就是在Flash Player 10.1发布后引入的"UncaughtErrorEvent"。

Flash Player UncaughtErrorEvent

此外,还有一个非常好的库可以处理这个问题。在这里:

Global Error Handler

最后,如果你看一下Flash Player开发的未来路线图,你会注意到静态类型、重载和许多大的变化即将到来。但目前来说,这是我使用过的最好的解决方案。

Flash Player Roadmap


UncaughtErrorEvents 应该只是在出现严重问题时优雅地退出应用程序的最后手段,而不应该成为您主要的错误处理策略!如果您使用一个集中的类来处理所有错误,那么您最终会得到一个庞大的怪物,破坏所有依赖管理,并使您的代码难以维护。此外,错误可能发生的原因有很多 - OP 问及编译时间检查。同步任务,如网络访问、回调等,都有自己的错误事件,应该使用它们来处理各自的原因。 - weltraumpirat
我知道UncaughtErrorEvent,但现在我们不能使用10.1播放器(根据我们收集的统计数据,一些用户没有它)。此外,我没有看到任何可以通过全局错误处理程序真正“处理”的错误。唯一可以这样做的事情是错误报告(我们已经有了UEE)。此外,这些报告在发布版本的Flash Player中没有关于异常发生位置的信息,所以我不得不实现一些手工堆栈跟踪。而且我确实做到了 :) 但重点是:应该“预防”错误而不是在报告到达后进行修复。 - Ilya Denisov

0

据我所见,它没有支持所请求的主要功能 - @Nullable / @NotNull。因此,这是一个离题的问题。我尝试运行了几次,但在这些报告中没有发现任何有趣的东西。而且,在有意违反规则的地方无法抑制警告 - 到目前为止对我来说毫无用处。也许我只是不够注意... - Ilya Denisov

0

在这种情况下最有用的应该是 getters 和 setters:

protected var _value:Object;

public function get value():Object{
    if(_value){
        return _value;
    }
    trace("missing object");// or throw "missing object error"
    return new Object();
}

public function set value(v:Object):void {
    _value = v;
}

编辑:

另外一个用于空值检查的附加方法是读取 describeType() 的 XML 并检查是否存在可访问的值。


是的,如果你只想获取一个属性值。我在答案中描述的概念适用于任何具有返回值的方法。 - weltraumpirat
但是您没有提到在这里可能非常有用的getter和setter。 - turbosqel

0
GUI编程的问题在于,总会存在某种状态。所描述的问题基本上是有一个有效和无效状态的问题。作为一名经验丰富的Flex开发人员,我可以告诉你,不存在所谓的无效状态。null是一个有效的引用,因此我们必须处理它。
我首先要问的问题是:为什么null是不好的?null是需要清理事物并确保我们获得没有数据的状态概念。
Flex有一个生命周期的概念,这基本上导致了非常有趣的行为。当组件被初始化时,它的子组件并没有被初始化。因此,乍一看,这是一个非常脆弱的系统。属性大多数情况下都委托给由视图聚合的其他组件(关于视图层)。但是同样的情况也可能发生在模型、控制器或服务层。
那么,如何解决这个问题?我认为这首先是一个设计决策。当null被看作是破坏系统的值时,默认情况下系统几乎已经破裂了。我认为null是指示缺少数据的最简单方法。
另一种方法是创建默认模型。从我的角度来看,模型是唯一真正的难点,因为所有其他组件都依赖于它们。
package net.icodeapps.examples.model
{
  public class User
  {
  }
}

package net.icodeapps.examples.model
{
  public class DefaultUser extends User
  {
  }
}

拥有这样的默认模型可以轻松地使用有效、非空引用和值启动应用程序,但也必须做出决策,无论是否正在处理默认模型。
另外,这是最重要的部分:如果系统没有经过适当的思考,将空值从应用程序中清除最终会耗尽内存。

有一个字段根本不应该为空(没有数据缺失的地方)。使用“默认”和“虚拟”对象只能防止错误进一步扩散(当异常发生时,处理某些事件的代码部分将不会同时执行一些已经执行的部分)。但这只是掩盖了问题 - 这种缺失根本不应该存在。通过使用@Nullable / @NotNull,可以向计算机解释您的意图,以便它可以在必要的知识下为您检查所有内容。同样的方式,编译器在指定变量类型时帮助您。 - Ilya Denisov
我完全理解这个想法。但实际上,这个问题可以很容易地回答:不,不,不。我只是想指出,有办法处理空值。这就是我想要补充的全部内容。 - Florian Salihovic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接