西西河

主题:【原创】Java 1.5新特点的一些体验 -- 老兵帅客

共:💬14 新:
全看树展主题 · 分页首页 上页
/ 1
下页 末页
家园 【原创】Java 1.5新特点的一些体验

这里的讨论顺序及所涉及的源代码来源于这样一本书:

Java 1.5 Tiger: A Developer's Notebook

By David Flanagan, Brett McLaughlin

Publisher : O'Reilly

Pub Date : June 2004

ISBN : 0-596-00738-8

Pages : 200

在这个话题里,我将就这本书中的一些例子进行讨论,探讨一下Java 1.5实现的优劣。这个话题将包括比较长的一系列子话题,为了读者查找方便,我这里将逐渐加入索引性链接。

我这个话题所使用的环境是Java 1.5和JBuilder 2005,其它的环境组合我没有试过,但是在我这个环境里,所列表的代码都是可以正常工作的。

家园 热烈欢迎!
家园 1.1 Working with Arrays

Arrays并不是在Java 1.5中新出现的,但是Java 1.5确实给它增加了很多Utility Methods,下面是一个简单的列表和说明:

1。deepEquals:Returns true if the two specified arrays are deeply equal to one another.Two array references are considered deeply equal if both are null, or if they refer to arrays that contain the same number of elements and all corresponding pairs of elements in the two arrays are deeply equal.

2。deepHashCode:Returns a hash code based on the "deep contents" of the specified array. If the array contains other arrays as elements, the hash code is based on their contents and so on, ad infinitum. For any two arrays a and b such that Arrays.deepEquals(a, b), it is also the case that Arrays.deepHashCode(a) == Arrays.deepHashCode(b).

3。deepToString:Returns a string representation of the "deep contents" of the specified array. If the array contains other arrays as elements, the string representation contains their contents and so on. This method is designed for converting multidimensional arrays to strings.

4。toString:Returns a string representation of the contents of the specified array. The string representation consists of a list of the array's elements, enclosed in square brackets ("[]"). Adjacent elements are separated by the characters ", " (a comma followed by a space). Elements are converted to strings as by String.valueOf(short). Returns "null" if a is null.

评论:上面四个方法中前三个都属于deep系列,它们的主要用处是处理多位数组下的判等、Hash和字符串化,但是说明文字中没有说明如果出现数组元素的递归自访问将如何处理;toString应该说是对以前版本缺陷的一个补偿。

下面是一个简单的例子,说明了该类不同方法的一些使用:

package test2005_2;

import java.util.Arrays;

import java.util.List;

public class ArraysTester {

private int[] ar;

public ArraysTester(int numValues) {

ar = new int[numValues];

for (int i = 0; i < ar.length; i++) {

ar[i] = (1000 - (300 + i));

}

}

public int[] get() {

return ar;

}

public static void main(String[] args) {

ArraysTester tester = new ArraysTester(50);

int[] myArray = tester.get();

// Compare two arrays

int[] myOtherArray = tester.get().clone();

if (Arrays.equals(myArray, myOtherArray)) {

System.out.println("The two arrays are equal!");

} else {

System.out.println("The two arrays are not equal!");

}

// Fill up some values

Arrays.fill(myOtherArray, 2, 10, new Double(Math.PI).intValue());

myArray[30] = 98;

// Print array, as is

System.out.println("Here's the unsorted array...");

System.out.println(Arrays.toString(myArray));

System.out.println();

// Sort the array

Arrays.sort(myArray);

// print array, sorted

System.out.println("Here's the sorted array...");

System.out.println(Arrays.toString(myArray));

System.out.println();

// Get the index of a particular value

int index = Arrays.binarySearch(myArray, 98);

System.out.println("98 is located in the array at index " + index);

String[][] ticTacToe = { {"X", "O", "O"},

{"O", "X", "X"},

{"X", "O", "X"}

};

System.out.println(Arrays.deepToString(ticTacToe));

String[][] ticTacToe2 = { {"O", "O", "X"},

{"O", "X", "X"},

{"X", "O", "X"}

};

String[][] ticTacToe3 = { {"X", "O", "O"},

{"O", "X", "X"},

{"X", "O", "X"}

};

if (Arrays.deepEquals(ticTacToe, ticTacToe2)) {

System.out.println("Boards 1 and 2 are equal.");

} else {

System.out.println("Boards 1 and 2 are not equal.");

}

if (Arrays.deepEquals(ticTacToe, ticTacToe3)) {

System.out.println("Boards 1 and 3 are equal.");

} else {

System.out.println("Boards 1 and 3 are not equal.");

}

}

}

家园 1.2 Using Queues

在Java 1.5中,我们终于看到了Queue,而且这个Queue还是支持Template的!有趣的是这个类可以自动地支持Template和常规情况。虽然下面的例子中并没有显式地使用Template,但是对应的方法确实可以使用Template。

package test2005_2;

import java.io.IOException;

import java.io.PrintStream;

import java.util.LinkedList;

import java.util.Queue;

public class QueueTester {

public Queue q;

public QueueTester() {

q = new LinkedList();

}

public void testFIFO(PrintStream out) throws IOException {

q.add("First");

q.add("Second");

q.add("Third");

Object o;

while ((o = q.poll()) != null) {

out.println(o);

}

}

public static void main(String[] args) {

QueueTester tester = new QueueTester();

try {

tester.testFIFO(System.out);

} catch (IOException e) {

e.printStackTrace();

}

}

}

家园 1.3 Ordering Queues Using Comparators

有些时候,我们需要使用排序的Queue,这就是PriorityQueue。在PriorityQueue中我们需要使用Comparator来确定排序的方法,其思路和C++ STL中的对应部分是类似的,但是实现上却值得推敲。

下面我先列表例子源代码,然后再讨论这个实现所存在的问题。

package test2005_2;

import java.util.Comparator;

import java.util.PriorityQueue;

import java.util.Queue;

public class PriorityQueueTester {

public static void main(String[] args) {

PriorityQueue<Integer> pq =

new PriorityQueue<Integer>(20,

new Comparator<Integer>() {

public int compare(Integer i, Integer j) {

int result = i % 2 - j % 2;

if (result == 0) {

result = i - j;

}

return result;

}

}

);

// Fill up with data, in an odd order

for (int i = 0; i < 20; i++) {

pq.offer(20 - i);

}

// Print out and check ordering

for (int i = 0; i < 20; i++) {

System.out.println(pq.poll());

}

}

}

我认为所存在的问题是对Comparator方法compare的定义方法。Comparator是一个Interface,实际使用的时候必须定义compare方法的实现。在C++ STL中,我们可以使用Object Method和Unary Object来比较优雅地定义它,但是在目前的Java实现中,似乎没有与Object Method和Unary Object相对应的东西,这样我们就只能够老老实实地将方法compare的实现写在Comparator的直接后面,不管这个方法有多复杂。这样做存在至少两个问题,一个是多次重写相同的代码而导致代码的膨胀和难以维护,另一个就是破坏了代码的模块性。看来Java虽然学到了C++的Template,但是却没有学到C++的STL。

家园 .NET中有类似的东西。由于.NET使用delegate,而不是

Interface,所以在Object Method问题上,比Java有些优势!

家园 2. Generics

Java 1.5中的Generics和C++的Template其实现几乎是一样的,也就是说,如果读者熟悉C++的Template,它也就熟悉Java 1.5中的Generics。但是Java 1.5中的Generics也有一些有趣的地方:

一。Type Wildcards,就是Template的具体类型不定。下面是一个例子。

public void printList(List<?> list, PrintStream out) throws IOException {

for (Iterator<?> i = list.iterator( ); i.hasNext( ); ) {

out.println(i.next( ).toString( ));

}

}

在这个例子中,我们可以发现Template的具体类型没有确定而依然还是个?虽然这意味着更多的不确定性和错误,但是它也确实提供了更多的灵活度。(注:这个例子在我的环境中没有通过检查,虽然它在含义上是明确的)。

二。Restricting Type Parameters,也就是逐渐受到约束的类型参数。这是一个很好的改进,在我的记忆里,它比C++的Template做的要好。下面是一个例子:

package test2005_2;

import java.util.Iterator;

import java.util.ArrayList;

import java.util.List;

class Box<T> {

protected List<T> contents;

public Box() {

contents = new ArrayList<T>();

}

public int getSize() {

return contents.size();

}

public boolean isEmpty() {

return (contents.size() == 0);

}

public void add(T o) {

contents.add(o);

}

public T grab() {

if (!isEmpty()) {

return contents.remove(0);

} else {

return null;

}

}

}

public class NumberBox<N extends Number> extends Box<N> {

public NumberBox() {

super();

}

// Sum everything in the box

public double sum() {

double total = 0;

for (Iterator<N> i = contents.iterator(); i.hasNext(); ) {

total = total + i.next().doubleValue();

}

return total;

}

}

这里NumberBox<N extends Number>是对Box<N>的一个限定,它使得N必须是对Number的扩展。

相对应的,我们可以有以下两种改写的sum方法:

public static double sum(Box<? extends Number> box1,

Box<? extends Number> box2) {

double total = 0;

for (Iterator<? extends Number> i = box1.contents.iterator( );

i.hasNext( ); ) {

total = total + i.next( ).doubleValue( );

}

for (Iterator<? extends Number> i = box2.contents.iterator( );

i.hasNext( ); ) {

total = total + i.next( ).doubleValue( );

}

return total;

}

public static <A extends Number> double sum(Box<A> box1,

Box<A> box2) {

double total = 0;

for (Iterator<A> i = box1.contents.iterator( ); i.hasNext( ); ) {

total = total + i.next( ).doubleValue( );

}

for (Iterator<A> i = box2.contents.iterator( ); i.hasNext( ); ) {

total = total + i.next( ).doubleValue( );

}

return total;

}

这样做的一个副作用就是Java开始和C++一样,可以把人搞得发疯了。

家园 这里又一篇关于C# Generics的详细介绍,比较不错!

外链出处

家园 虽然俺完全不懂,但是看到这样真诚尽现的技术帖,一定要跟着版主叫个好,大力支持
家园 3. Enumerated Types

在Java 1.5中,我们终于看到了enum这个在其它语言中常见的东西。

虽然Java 1.5中enum的使用很简单,但是我不明白为什么Sun非要把enum构造成从java.lang.Enum类中衍生出来的一个子类,因为enum思想本身是很简单的,它不过是一个整数序列的助记符形式。用类自然可以很简单地实现enum功能,但是代价在于类的成本,不管类的构建成本有多低,它都肯定高于简单数据类型。也许问题的关键在于Java不存在可以脱离Class单独存在的数据部件,但是名义上的附属、实际上的简单实现我想应该不是件很困难的事情,也就是修改一下编译器,以实现那个整数序列罢了,实在是没有必要单独构造出一个完整的类java.lang.Enum来。书里的解释是可以增加可检查度以增强编译时间的检查能力,但是C++的enum实现应该也是可以做到编译时间的检查能力的。换句话来说就是优雅的成本。

下面是它的一些例子(但是我有所修改):

package test2005_2;

public class Student {

private String firstName;

private String lastName;

private Grade grade;

enum Grade {

A, B, C, D, F, INCOMPLETE} ;

public Student(String firstName, String lastName) {

this.firstName = firstName;

this.lastName = lastName;

}

public void setFirstName(String firstName) {

this.firstName = firstName;

}

public String getFirstName() {

return firstName;

}

public void setLastName(String lastName) {

this.lastName = lastName;

}

public String getLastName() {

return lastName;

}

public String getFullName() {

return new StringBuffer(firstName)

.append(" ")

.append(lastName)

.toString();

}

public void assignGrade(Grade grade) {

this.grade = grade;

}

public Grade getGrade() {

return grade;

}

}

在enum的当前实现中,Sun允许其拥有方法,一个很有趣的想法,但是有必要吗?也许根本原因是由于它本质上是一个类,自然什么都可以往里面加,但是请不要忘记,事情的本质是什么?另外,enum的定义中可以使用Template,一个可以把人搞疯的东西。

总之,我们看到了一个无比强大的enum,它实际上具有了类的一切能力,当然限制在public,static,和final的范围内。

家园 4. Autoboxing and Unboxing

这个功能的名义是很伟大的,其本质不过是简单数据类型和相应类数据类型(例如int和Integer)之间的自动转换。以前的版本需要人们手工地将数据在简单数据类型和相应类数据类型之间进行转换,这个版本所做到的不过是将这个过程自动化罢了。也就是说,尽管程序员在源代码的书写方面的确有所节省,但是编译器的工作却是自动加上了这个转换,因此从代码执行角度讲,这个改进的意义不大,尽管程序员会很欢迎它。

这个功能的使用很简单,就不附带例子了。

家园 5. varargs变长参数表

传统上处理变长参数的办法是将变化部分弄成数组,然后使用数组加数组长度的办法(例如C语言的Main函数)或者只使用数组(例如Java,因为Java的数组含有长度信息)来传入到方法中进行处理。但是这种方法的问题在于要求所有参数的类型是一致的,否则无法构成数组,除非都变成Object,但是这样做的成本有时候会比较高;而且这样做的一个代价就是无法有效地对实际参数进行检验。

Java 1.5的变长参数表的实现就是使用上述的方法,这样程序员就可以比较简单地处理变长参数表问题了,它的优点和缺点上面都已经涉及到了。下面是它的一个例子。

package com.oreilly.tiger.ch05;

public class Guitar {

private String builder;

private String model;

private float nutWidth;

private GuitarWood backSidesWood;

private GuitarWood topWood;

private GuitarInlay fretboardInlay;

private GuitarInlay topInlay;

private static final float DEFAULT_NUT_WIDTH = 1.6875f;

public Guitar(String builder, String model, String... features) {

this(builder, model, null, null, DEFAULT_NUT_WIDTH, null, null, features);

}

public Guitar(String builder, String model,

GuitarWood backSidesWood, GuitarWood topWood,

float nutWidth, String... features) {

this(builder, model, backSidesWood, topWood, nutWidth, null, null, features);

}

public Guitar(String builder, String model,

GuitarWood backSidesWood, GuitarWood topWood,

float nutWidth,

GuitarInlay fretboardInlay, GuitarInlay topInlay,

String... features) {

this.builder = builder;

this.model = model;

this.backSidesWood = backSidesWood;

this.topWood = topWood;

this.nutWidth = nutWidth;

this.fretboardInlay = fretboardInlay;

this.topInlay = topInlay;

}

}

家园 .NET的enum和Java不一样,不是Class.

An enumeration (enum) is a special form of value type, which inherits from System.Enum and supplies alternate names for the values of an underlying primitive type.

The following are the restrictions apply to an enum type in C#

1. They can’t define their own methods.

2. They can’t implement interfaces.

3. They can’t define properties or indexers.

In Tiger, the Java&#8482; programming language gets linguistic support for enumerated types. In their simplest form, these enums look just like their C, C++, and C# counterparts: 

enum Season { WINTER, SPRING, SUMMER, FALL }

But appearances can be deceiving. Java programming language enums are far more powerful than their counterparts in other languages, which are little more than glorified integers. The new enum declaration defines a full-fledged class (dubbed an enum type). In addition to solving all the problems mentioned above, it allows you to add arbitrary methods and fields to an enum type, to implement arbitrary interfaces, and more. Enum types provide high-quality implementations of all the Object methods. They are Comparable and Serializable, and the serial form is designed to withstand arbitrary changes in the enum type.

家园 我们是否应该使用这个Feature呢?Sun的观点是:

So when should you use varargs? As a client, you should take advantage of them whenever the API offers them. Important uses in core APIs include reflection, message formatting, and the new printf facility. As an API designer, you should use them sparingly, only when the benefit is truly compelling. Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called.

全看树展主题 · 分页首页 上页
/ 1
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河