java1.5可读性评论

发表于:2007-06-22来源:作者:点击数: 标签:
Java1.5 规范并没有很好得在新功能特性与程序可读性之间提供一个很好得平衡,事实上,所有得新功能都可以用一种清楚,明确,可读性良好并且与1.5之前的语法兼容的语法结构来表达.在这篇文章里,我会通过我所提议的三种语法来阐述这个观点(翻译到这里, will

   
  Java1.5 规范并没有很好得在新功能特性与程序可读性之间提供一个很好得平衡,事实上,所有得新功能都可以用一种清楚,明确,可读性良好并且与1.5之前的语法兼容的语法结构来表达.在这篇文章里,我会通过我所提议的三种语法来阐述这个观点(翻译到这里,

will不禁对本文作者肃然起敬,也预见到了本文的翻译难度不小…-_-bb..)



New for Loop
由于许多开发者都希望对于collections和arrays的循环操作能有更紧凑的形式,所以Sun提出了一个新的for语句,包含了foreach的语法含义.

旧语法:void cancelAll(Collection c) { for (Iterator i = c.iterator(); i.hasNext(); ) { TimerTask tt = (TimerTask) i.next(); tt.cancel(); }}
新语法:void cancelAll(Collection c) { for (Object o : c) ((TimerTask)o).cancel();}


在新语法中,对集合对象的迭代操作更加紧凑了,但是用冒号来表示foreach显得十分含糊.根据Sun的解释是foreach使用的太过普遍(它是Perl,VBScript,Ruby,PHP和Tcl等语言中的关键字), 因此如果将它归为关键字会给很多已完成的程序带来问题,因为这些程序有可能使用foreach作为变量名或是方法名. 这个理由是合理的,但这并不意味着没有其他比使用冒号更好的方法.



例如,为什么不添加一个eachof关键字,使得标准的for循环变为foreach循环,如下代码所示:

void cancelAll(Collection c) { for (Object o = eachof c) ((TimerTask)o).cancel();}
不像foreach, eachof在各种主要的脚本语言中作为关键字使用的并不多,而且对于少数不幸使用了eachof作为方法/变量名的,也有javac ?Csource帮忙.

除了能更好的向后兼容关键字,这个方法还有以下几个好处:

? Eachof也适用于其他的循环结构,尤其是对多个集合对象同时进行查询.例如:
void cancelAll(Collection c, d, e) {
assert c.size() == d.size() == e.size();
while ((Object o = eachof c) != null)
{
((TimerTask)o).cancel();
((TimerTask) eachof d).cancel();
((TimerTask) eachof e).cancel();
}
}



? eachof的可读性好,容易理解,与break和continue关键字一样,它作用于最近的循环体.
以上是我的提议,其他朋友也可以使用另外的方法,关键是只要多花点时间,就可以发现更好的方法,仅仅是为了向后兼容而使用冒号来实现foreach循环,并不是个好方法.

Generics
Java1.5中最主要的特性就是泛函.通过它可以极大的增强静态类型检查功能,而且简化了许多对于collection对象的繁琐操作.有人认为Java应该遵循C++和STL中所使用的泛函语法,如下所示:

1.5之前的实现static void expurgate(Collection c) { for (Iterator i = c.iterator(); i.hasNext(); ) { String s = (String) i.next(); if(s.length() == 4) i.remove(); }}
1.5+的实现static void expurgate(Collection c) { for (Iterator i = c.iterator(); i.hasNext(); ) if (i.next().length() == 4) i.remove();}

然而java之所以成功,其中的一个原因就是James Gosling吸取了C++中的教训,例如取消了类实现的多继承.在实现泛函上,java应该继续采用这个方法,用简单清晰的语法和语义来实现Generic.

例如,许多程序员喜欢用花括号来表示泛函语法.Sun的提议认为这样会混淆类型参数与数值参数,再多想一点,我们可以发现另外一个方法:将类型参数用一对括号包住,然后放在被类型参数所作用的元素前,如下代码所示:
static void expurgate((String) Collection c) {
for ((String) Iterator i = c.iterator(); i.hasNext(); )
if (i.next().length() == 4)
i.remove();
}



这样似乎与类型转换又混在一起了,但是他们是出现在不同的上下文中的.而且,假如Java Generic真的如此实现的,也不会导致将此结构误认为类型转换.

可以将它理解为“Declaration Cast”,即在某处做一类型声明来告诉编译器,当从集合中取出某一元素时,自动(悄悄的..J)进行什么样的类型转换(这段翻译的will好别扭,不过总算说通顺了…)

根据这种提议,类型转换操作符将一般化为类型表达式. 在赋值操作的上下文里,它表达类型转换,而在类型声明的上下文里,它描述的是泛函类型参数.两种用法并不冲突.

有趣的是,这种方法类似与数组的声明.例如:
String [] strings1 = new String [10];
(String) List strings2 = new (String) List;



List与数组变量的声明语法上非常类似,除了数组声明中缺少了一对括号.假如Java1.5中添加了静态类型安全数组(谁能告诉will这是什么??),那么只要通过添加一对括号,就可以将它和标准数组区别开,从而统一了数组声明与集合声明的语法.

总的来说,上面的这种语法有如下几点好处(inverted generics syntax该怎么翻译?)

? 它尽量使用了已有的语法结构,程序员原有的对类型转换的认识有助于认识新的语法

? 对比尖括号有更好的可读性.当然,这只是个人的喜好.

? 不会带来类型参数和参数变量重叠书写.

? 它反映了在泛型系统中实际的发生的故事

? 它统一了泛型集合和数组的声明语法

下面的例子集合了上面我所提议的两种语法实现

Sun的实现方法void cancelAll(Collection c) { for (TimerTask task : c) task.cancel();}
我提议的方法:void cancelAll((Timertask) Collection c) { for (TimerTask task = eachof c) task.cancel();}

Variance
Java1.5最新提出的特点是Variance(如何翻译?),它修正了java的类型系统中的一些缺陷,并且增加了静态类型检查的力度.关于Variance的具体介绍,大家可以参考Sun的官方文档,我这里只是关注它的语法.

Variance的语法如下:

A covariant, or read-only list of Numbers as List<+Number>.
A contravariant, or write-only list of Numbers as List<-Number>.
A bivariant, read-write list of Numbers as List<*>.
An invariant list of Numbers as List<=Number>.
静态安全数组的语法类似.

我认为这种语法是会令程序员感到困惑的,特别是那些对于variance的概念仍然不清楚的程序员(包括了正在翻译的will)

我们知道,类的层次关系的表示是与习惯的阅读顺序一致的.例如,..Number表示从Number的所有超类到Number类,Number..代表从Number类到它的所有子类,Number相对两点省略号的位置可以表明描述该类层次结构的方向.

在java或其他类C语言里,也是使用相类似的机制来描述先加后操作(++i)与先操作后加(i++)

下面的例子来自variance tutorial,用我所提议的语法结构重写了一次
public abstract class Shape {
public abstract void drawOn (Canvas c)
}

public class Line extends Shape {
public int x, y, x2, y2
public void drawOn (Canvas c) { ... }
}

public class Polygon extends Shape {
private (Line) List lines;
public void drawOn (Canvas c) { ... }
}

public class Canvas {
public void draw (Shape s) {
s.drawOn (this)
}

// Covariant (read-only) list of shapes
public void drawAll ((Shape..) List shapes) {
for (Shape s = eachof shapes)
s.drawOn (this)
}
}

...

// copy from covariant list (read-only) to contravariant list (write-only)
public void copy ((Shape..) List from, (..Shape) List to) {
for (Shape s = eachof from)
to.add (s);
}

// copy from covariant array (read-only) to contravariant array (write-only)
public void copy ((Shape..)[] from, (..Shape)[] to) {
int i = 0;
for (Shape s = eachof from)
to[i++] = s;
}



这种写法有以下好处:

? 形式紧凑,特别是有利于复杂的类型表达式.Shap..表示covariance,..Shape表示contravariance.

? 对于初学者,可以使用更清楚的语法:Shape..final,Object..Shape.而不需要去了解底层的类型理论.

? Javadoc可以给位置的表示方法提供更好的支持,从左到右的显示类层次中,从子类到父类的关系.

原文:http://www.onjava.com/pub/a/onjava/2003/09/24/readable_java.html

原文转自:http://www.ltesting.net