现在的位置: 首页 > 综合 > 正文

Android开源项目-编码风格规范-Code Style Guidelines for Contributors

2013年09月28日 ⁄ 综合 ⁄ 共 6047字 ⁄ 字号 评论关闭

Code Style Guidelines for Contributors

版本:Android 4.0 r1

英文原文:http://source.android.com/source/code-style.html

以下规则并非指导或推荐的性质,而是必须遵守的规定。如果不遵守这些规定,Android通常不会接受投稿。

已有的代码未必全部遵守了这些规定,但是新的代码全部都应该遵守。

在本文中

  撰稿人的编码规范

  
Java语言规范

不要忽略Exceptions

不要捕获顶级Exception

不要使用Finalizer

使用完全限定名称的Import

Java类库规范

Java编码规范

使用Javadoc标准的注释

编写简短的方法

在标准的位置定义字段

限制变量的作用范围

Import语句排序

使用空格进行缩进

遵守字段命名惯例

使用标准的大括号风格

限制代码行长度

使用标准的Java Annotation

简称等同于单词

使用TODO注释

慎用日志

保持一致

Javatest风格规范

遵守测试方法命名规范

Java语言规范

我们遵循标准的Java编码规范,并加入了新的规则:

不要忽略异常

有时,完全忽略异常是非常诱人的,比如:

void setServerPort(String value) {

    try {

        serverPort = Integer.parseInt(value);

    } catch (NumberFormatException e) { }

}

绝对不要这么做。也许你会认为:你的代码永远不会碰到这种出错的情况,或者处理异常并不重要,可类似上述忽略异常的代码将会在代码中埋下一颗地雷,说不定哪天它就会炸到某个人了。你必须在代码中以某种规矩来处理所有的异常。根据情况的不同,处理的方式也会不一样。

无论何时,空的catch语句都会让人感到不寒而栗。虽然很多情况下确实是一切正常,但至少你不得不去忧虑它。在Java中你无法逃离这种恐惧感。
-James Gosling

可接受的替代方案包括(按照推荐顺序):

·
向方法的调用者抛出异常。

       
void setServerPort(String value) throws NumberFormatException {

       
    serverPort = Integer.parseInt(value);

       
}

·
根据抽象级别抛出新的异常。

       
void setServerPort(String value) throws ConfigurationException {

       
    try {

       
        serverPort = Integer.parseInt(value);

       
    } catch (NumberFormatException e) {

       
        throw new ConfigurationException("Port " + value + " is not valid.");

       
    }

       
}

·
默默地处理错误并在
catch {}语句块中替换为合适的值。

       
void setServerPort(String value) {

       
    try {

       
        serverPort = Integer.parseInt(value);

       
    } catch (NumberFormatException e) {

       
        serverPort = 80;  // default port for server

       
    }

       
}

·
捕获异常并抛出一个新的
RuntimeException。这种做法比较危险:只有确信发生该错误时最合适的做法就是崩溃,才会这么做。

       
void setServerPort(String value) {

       
    try {

       
        serverPort = Integer.parseInt(value);

       
    } catch (NumberFormatException e) {

       
        throw new RuntimeException("port " + value " is invalid, ", e);

       
    }

       
}

请记住,最初的异常是传递给构造方法的RuntimeException。如果代码必须在Java 1.3版本下编译,需要忽略该异常。

·
最后一招:如果确信忽略异常比较合适,那就忽略吧,但必须把理想的原因注释出来:

       
void setServerPort(String value) {

       
    try {

       
        serverPort = Integer.parseInt(value);

       
    } catch (NumberFormatException e) {

       
        // Method is documented to just ignore invalid user input.

       
        // serverPort will just be unchanged.

       
    }

       
}

不要捕获顶级的Exception

有时在捕获Exception时偷懒也是很吸引人的,类似如下的处理方式:

try {

    someComplicatedIOFunction();        // may throw IOException

    someComplicatedParsingFunction();   // may throw ParsingException

    someComplicatedSecurityFunction();  // may throw SecurityException

    // phew, made it all the way

} catch (Exception e) {                 // I'll just catch all exceptions

    handleError();                      // with one generic handler!

}

不要这么做。绝大部分情况下,捕获顶级的ExceptionThrowable都是不合适的,Throwable更不合适,因为它还包含了Error异常。这种捕获非常危险。这意味着本来不必考虑的Exception(包括类似ClassCastExceptionRuntimeException)被卷入到应用程序级的错误处理中来。这会让代码运行的错误变得模糊不清。这意味着,假如别人在你调用的代码中加入了新的异常,编译器将无法帮助你识别出各种不同的错误类型。绝大部分情况下,无论如何你都不应该用同一种方式来处理各种不同类型的异常。

本规则也有极少数例外情况:期望捕获所有类型错误的特定的测试代码和顶层代码(为了阻止这些错误在用户界面上显示出来,或者保持批量工作的运行)。这种情况下可以捕获顶级的Exception(或Throwable)并进行相应的错误处理。在开始之前,你应该非常仔细地考虑一下,并在注释中解释清楚为什么这么做是安全的。

比捕获顶级Exception更好的方案:

·
分开捕获每一种异常,在一条
try语句后面跟随多个catch
语句块。这样可能会有点别扭,但总比捕获所有Exception要好些。请小心别在catch语句块中重复执行大量的代码。

·
重新组织一下代码,使用多个
try块,使错误处理的粒度更细一些。把IO从解析内容的代码中分离出来,根据各自的情况进行单独的错误处理。

·
再次抛出异常。很多时候在你这个级别根本就没必要捕获这个异常,只要让方法抛出该异常即可。

请记住:异常是你的朋友!当编译器指出你没有捕获某个异常时,请不要皱眉头。而应该微笑:编译器帮助你找到了代码中的运行时(runtime)问题。

不要使用Finalizer

Finalizer提供了一个机会,可以让对象被垃圾回收器回收时执行一些代码。

优点:便于执行清理工作,特别是针对外部资源。

缺点:调用finalizer的时机并不确定,甚至根本就不会调用。

结论:我们不要使用finalizers。大多数情况下,可以用优秀的异常处理代码来执行那些要放入finalizer的工作。如果确实是需要使用finalizer,那就定义一个close()方法(或类似的方法),并且在文档中准确地记录下需要调用该方法的时机。相关例程可以参见InputStream。这种情况下还是适合使用finalizer的,但不需要在finalizer中输出日志信息,因为日志不能因为这个而被撑爆。

使用完全限定Import

当需要使用foo包中的Bar类时,存在两种可能的import方式:

1.  
import foo.*;

优点:可能会减少import语句。

1.  
import foo.Bar;

优点:实际用到的类一清二楚。代码的可读性更好,便于维护。

结论:用后一种写法来import所有的Android代码。不过导入java标准库(java.util.*java.io.*)
和单元测试代码
(
junit.framework.*)时可以例外。

Java类库规范

使用Android Java类库和工具存在一些惯例。有时这些惯例会作出重大变化,可之前的代码也许会用到过时的模板或类库。如果用到这部分过时的代码,沿用已有的风格就是了(参阅Consistency)。创建新的组件时就不要再使用过时的类库了。

Java编程规范

使用Javadoc标准注释

每个文件的开头都应该有一句版权说明。然后下面应该是package包语句和import语句,每个语句块之间用空行分隔。然后是类或接口的定义。在Javadoc注释中,应描述类或接口的用途。

package com.android.internal.foo;

 

import android.os.Blah;

import android.view.Yada;

 

import java.sql.ResultSet;

import java.sql.SQLException;

 

public class Foo {

    ...

}

每个类和自建的public方法必须包含Javadoc注释,注释至少要包含描述该类或方法用途的语句。并且该语句应该用第三人称的动词形式来开头。

例如:

static double sqrt(double a) {

    ...

}

public String(byte[] bytes) {

    ...

}

如果所有的Javadoc都会写成“sets Foo”,对于那些无关紧要的类似setFoo()getset语句是不必撰写Javadoc的。如果方法执行了比较复杂的操作(比如执行强制约束或者产生很重要的副作用),那就必须进行注释。如果“Foo”属性的意义不容易理解,也应该进行注释。

无论是public的还是其它类型的,所有自建的方法都将受益于Javadocpublic的方法是API的组成部分,因此更需要Javadoc

Android目前还没有规定自己的Javadoc注释撰写规范,但是应该遵守Sun
Javadoc
约定

编写简短的方法

为了把规模控制在合理范围内,方法应该保持简短和重点突出。不过,有时较长的方法也是合适的,所以对方法的代码长度并没有硬性的限制。如果方法代码超过了40行,就该考虑是否可以在不损害程序结构的前提下进行分拆。

在标准的位置定义字段

字段应该定义在文件开头,或者紧挨着使用这些字段的方法之前。

限制变量的作用范围

局部变量的作用范围应该是限制为最小的(Effective Java29条)。使用局部变量,可以增加代码的可读性和可维护性,并且降低发生错误的可能性。每个变量都应该在最小范围的代码块中进行声明,该代码块的大小只要能够包含所有对该变量的使用即可。

应该在第一次用到局部变量的地方对其进行声明。几乎所有局部变量声明都应该进行初始化。如果还缺少足够的信息来正确地初始化变量,那就应该推迟声明,直至可以初始化为止。

本规则存在一个例外,就是涉及try-catch语句的情况。如果变量是用方法的返回值来初始化的,而该方法可能会抛出一个checked异常,那么必须在try块中进行变量声明。如果需在try块之外使用该变量,那它就必须在try块之前就进行声明了,这时它是不可能进行正确的初始化的。

// Instantiate class cl, which represents some sort of Set

Set s = null;

try {

    s = (Set) cl.newInstance();

} catch(IllegalAccessException e) {

    throw new IllegalArgumentException(cl + " not accessible");

} catch(InstantiationException e) {

    throw new IllegalArgumentException(cl + " not instantiable");

}

 

// Exercise the set

s.addAll(Arrays.asList(args));

但即便是这种情况也是可以避免的,把try-catch
块封装在一个方法内即可:

Set createSet(Class cl) {

    // Instantiate class cl, which represents some sort of Set

    try {

        return (Set) cl.newInstance();

    } catch(IllegalAccessException e) {

        throw new IllegalArgumentException(cl + " not accessible");

    } catch(InstantiationException e) {

        throw new IllegalArgumentException(cl + " not instantiable");

    }

}

 

...

 

// Exercise the set

Set s = createSet(cl);

s.addAll(Arrays.asList(args));

除非理由十分充分,否则循环变量都应该在for语句内进行声明,:

for (int i = 0; i n; i++) {

    doSomething(i);

}

for (Iterator i = c.iterator(); i.hasNext(); ) {

    doSomethingElse(i.next());

}

对Import语句排序

import语句的次序应该如下:

1.   Android imports

2.  
第三方库(comjunitnetorg

3.  
javajavax

为了精确匹配

抱歉!评论已关闭.