语法改动比较大的是泛型编程。使用泛型编程需要在声明的时候提供附加的声明信息。比如:
List words =
new ArrayList();
需要替换成:
List<String> words = new
ArrayList<String>();
这样做的一个优点是,如果你插入数组的数据类型不是字符串的话,你就可以在编译的时候发现和解决这个bug。如果不使用上面的声明,这个bug不可能在编译的时候发现,程序运行后会出现ClassCastException
的错误。
另一个好处是:你不在需要担心集合中的元素超出了范围:
String title = ((String)
words.get(i)).toUppercase();
使用:
String title =
words.get(i).toUppercase();
你能简单的介绍一下这些J2SE 1.5中改变最大的六个方面吗?
好的,
• 泛型编程
– 提供集合对象的编译时安全类型检查。
• 增强 for 循环 - 编程更容易,去掉了修正了导致迭代出错的问题。
• 装箱/拆箱 -
原始类型(int)和封装类型(Integer)的转换更容易。
• 类型安全的枚举 – 提供了最常使用的类型安全的枚举模式。(Effective Java,
Item 21)
• 静态导入Static import - Lets you avoid qualifying static members with
class names, without the shortcomings of the Constant Interface antipattern
(Effective Java, Item 17).
• Metadata –
避免编写描述信息的代码,实现“声明”编程的模式。程序员声明需要做什么,然后由相关的工具来完成具体的工作。
一、泛型编程:
比如,过滤一个集合中的元素,现在的做法和j2se1.5中的做法有什么不同呢?
现在的做法是:
/**
*
从一个指定的集合中去掉一个4个字符的元素。
*/
static void expurgate(Collection c) {
for
(Iterator i = c.iterator(); i.hasNext(); ) {
String s = (String)
i.next();
if(s.length() ==
4)
i.remove();
}}
上面的代码,有些缺陷,在运行的过程中可能出错。比如:在集合中如果包含一个StringBuffer类型的数据。
以后可以这样做:
static
void expurgate(Collection<String> c) {
for (Iterator<String> i =
c.iterator(); i.hasNext(); )
if (i.next().length() ==
4)
i.remove();
}
二、增强的for循环!
一个集合中元素的迭代,原来的做法繁琐。J2SE1.5中大多数情况下你不需要使用Iterate
来遍历一个集合。增强的for循环,让编译器来完成具体的迭代工作。比如:
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).close();
}
注意:上面的冒号,它表示:in。在C#中或者很自然的一个替代是:foreach
和in
。但是考虑到兼容性,我们没有那样做。
泛型编程和增强的for结合后会是什么结果呢?
上面的例子中的代码,可以用下面的代码表示:
void
cancelAll(Collection<TimerTask> c) {
for (TimerTask task :
c)
task.cancel();
}
三、什么是装箱?
大家知道,java语言中有两种数据类型:一些是基本数据类型,另一些是对象引用类型。基本的数据类型无法直接放入到集合中,除非做相应的类型转换。这种转换非常枯燥。
举例:map数据类型的key用来存储单词,value用来存储单词重复的次数。这是一个计算单词出现频率的小程序。
public
class Freq {
private static final Integer ONE = new Integer(1);
public
static void main(String args[]) {
Map m = new TreeMap();
for (int i=0;
i<args.length; i ) {
Integer freq = (Integer)
m.get(args[i]);
m.put(args[i], (freq==null ? ONE :new Integer(freq.intValue()
1)));
}
System.out.println(m);
}
}
下面是采用装箱,泛型,和增强的for循环后的代码:
public
class Freq {
public static void main(String args[]) {
Map<String,
Integer> m = new TreeMap<String, Integer>();
for (String word :
args)
m.put(word, m.get(word)
1);
System.out.println(m);
}
}
需要注意:上面的程序假定拆箱为null的时候,值为0。
J2SE1.5的新特点(下)
类型安全的枚举比以前的枚举有什么优点呢?
有如下特点:
•
提供编译时int枚举的安全检查,同时不再提供其他类型安全检查。
• 提供了枚举的命名空间
• 可以直接把它们放到集合中。
•
因为他们本质上是类,你可以向里面添加属性和方法。
上面的这些特点的确不错。能讲讲类型安全的枚举语言的特点和类型安全的枚举模式的关系吗?
一般来说:上面的那些特点简单的从语义上支持了模式。看下面的例子,和C/C++
枚举的声明很相似:
enum Season { winter, spring, summer, fall
}
声明虽然相似,但是确让编译器实现了上面提到的许多特性。你还可以把Season用到switch的判断语句中。
请举例说明“类型安全的枚举”的优点。
下面是一个表示美分枚举类型的例子。
public
enum Coin {
penny(1), nickel(5), dime(10), quarter(25);
Coin(int value) {
this.value = value; }
private final int value;
public int value() { return
value;
}
}
这是个创举。我们定义了value来作为读取Coin的公共变量。在枚举的构造函数中可以在声明枚举实例的时候来初始化它。
让我们看看进一步使用这个枚举的例子。
我把不的呢。下面的程序打印一个表格和其中coin的大小和颜色。
public
class CoinTest {
public static void main(String[] args) {
for (Coin c :
Coin.VALUES)
System.out.println(c + ": "t"
+ c.value() +"¢ "t" +
color(c));
}
private enum CoinColor { copper, nickel, silver }
private
static CoinColor color(Coin c) {
switch(c) {
case Coin.penny: return
CoinColor.copper;
case Coin.nickel: return CoinColor.nickel;
case
Coin.dime:
case Coin.quarter: return CoinColor.silver;
default: throw new
AssertionError("Unknown coin: " + c);
}
}
}
太棒了。那么静态引入static import
的功能对程序员有什么帮助呢?
首先他避免了程序员使用前缀的静态成员。以前常见的的一个替代的做法是:
// "Constant Interface"
antipattern – 不推荐的做法
public interface Physics {
public static final double
AVOGADROS_NUMBER = 6.02214199e23;
public static final double
BOLTZMANN_CONSTANT = 1.3806503e-23;
public static final double
ELECTRON_MASS = 9.10938188e-31;
}
public class Guacamole implements Physics {
public
static void main(String[] args) {
double moles = ...;
double molecules =
AVOGADROS_NUMBER *
moles;
...
}
}
上面的做法达到了效果,可是却违背了一些设计原则。接口是用来定义方法和类型的,不是提供常量声明的。而且只是由Guacamole使用的常量也暴露给了使用其的客户端。
静态引入功能提供了一个简单的实现。这个功能和包的import功能类似。
import
static org.iso.Physics.*;
class Guacamole {
public static void main(String[]
args) {
double molecules = AVOGADROS_NUMBER *
moles;
...
}
}
明白,那么什么是元数据类型metadata的功能呢?
使用metadata和第三方工具提供商可以让程序员自己的日子好过点。
以前许多发布API需要大量的描述信息。比如:定义一个JAX-RPC
网络服务API你需要提供其接口和实现类。如下:
public interface CoffeeOrderIF extends
java.rmi.Remote {
public Coffee [] getPriceList()
throws
java.rmi.RemoteException;
public String orderCoffee(String name, int
quantity)
throws java.rmi.RemoteException;
}
public class CoffeeOrderImpl implements CoffeeOrderIF
{
public Coffee [] getPriceList() {
...
}
public String
orderCoffee(String name, int quantity)
{
...
}
}
使用元数据功能,你可以节省大量的工作。你所需要做的是在代码上加上特殊的注释。你所使用的开发工具可是使用这些注释自动生成相关的代吗。如下所示:
import
javax.xml.rpc.*;
public class CoffeeOrder {
@Remote public Coffee []
getPriceList() {
...
}
@Remote public String orderCoffee(String name,
int quantity) {
...
}
}