EDN首页   博客首页 用户登陆  |  注册
aaa
发表于 2008/11/14 20:51:15

0

关于投票

原创java连载--泛型(7)

类型擦除(Type Erasure)

       当我们实例化一个泛型的时候,编译器使用一种叫做类型擦除(type erasure)的技术。在类型擦除的过程中,编译器会去除掉 类与接口中所有和类型参数有关的信息。类型擦除使得用泛型的java应用程序能够和该泛型创建之前就存在的java库和应用程序相兼容。

       例如Box<String>在编译的时候产生一个叫做原型(raw type)的类型Box,而所谓原型就是没有任何参数的泛型类名或者接口名。这也就是说,在运行的时候我们不知道一个泛型类究竟是什么类型的对象。如下的代码在编译的时候就会报错:

public class MyClass<E> {

    public static void myMethod(Object item) {

        if (item instanceof E) {  //Compiler error

            ...

        }

        E item2 = new E();   //Compiler error

        E[] iArray = new E[10]; //Compiler error

        E obj = (E)new Object(); //Unchecked cast warning

    }

}

       上面黑体代码之所以在编译的时候会报错是因为编译器去除了所有和参数(由类型参数E代表)相关的信息。

       有了类型擦除技术之后,就可以让新的代码和遗留的代码共存。但是无论如何,使用原型是一种不好的编程习惯,应该避免在程序中使用。当把遗留代码和泛型代码混合在一起的时候,我们可能会碰到类似于下面的告警:

Note: WarningDemo.java uses unchecked or unsafe operations.

Note: Recompile with -Xlint:unchecked for details.

       例如,我们用一个旧的API,但参数却用的是一个原型参数,如下的代码所示:

public class WarningDemo {

    public static void main(String[] args){

        Box<Integer> bi;

        bi = createBox();

    }

 

    static Box createBox(){

        return new Box();

    }

}

       我们用 -Xlintunchecked 重新编译就会显示出如下附加的信息:

WarningDemo.java:4: warning: [unchecked] unchecked conversion

found   : Box

required: Box<java.lang.Integer>

        bi = createBox();

                      ^

1 warning

 

系统分类: 软件开发  |  用户分类: Java学习  |  标签: java 类型擦除  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(512) | 回复(0)

发表于 2008/11/13 22:08:08

0

关于投票

原创java连载--泛型(6)

通配符

       在泛型中,我们可以用一个通配符来代替一个未知的类型。例如,使用下面的代码为某种animal指定一个cage

Cage<? extends Animal> someCage = ...;

       "? extends Animal"表示一种未知的类型,它可能是animal的一种子类型,也可能是animal自己本身,总的来讲就是某种animal。上面的例子是一种受限通配符,它的上限就是Animal。如果需要装下某种animalcage,那么就可以被用作是lion cage或者butterfly cage

如果使用super而不是extends则就可以为未知类型指定一个下限(lower bound)。例如,Cage<? super Animal>表示的也是一种未知类型,其可能是animal的一种超类型(supertype),也可能是animal自己本身。当然,如果我们用<?>来定义一个未知类型,那么这样的未知类型是不受限的。一个不受限的未知类型实质上就是<? extends Object>

       虽然Cage<Lion>Cage<Butterfly>不是Cage<Animal>的子类型,但是却是Cage<? extends Animal>的子类型。上面已经定义了someCage,那么就可以进行如下赋值:

              Cage<Lion> lionCage = ...;

Cage<Butterfly> butterflyCage = ...;

someCage = lionCage; // OK

              someCage = butterflyCage; // OK

       但是我们还是不能把butterflieslions直接addsomeCage

interface Lion extends Animal {}

Lion king = ...;

interface Butterfly extends Animal {}

Butterfly monarch = ...;

someCage.add(king);     // compiler-time error

       someCage.add(monarch);      // compiler-time error

       如果someCage是一个butterfly cage,那么它装入butterfly是没有问题的,但是却装不了lion。当然,如果someCage是一个lion cage,那么它装入lion是没有问题的,却装不了butterfly。也就是我们不能向someCage种装入任何anmial,那么是不是someCage就没有任何用了呢?其实不然,例如下面的代码就用到了someCage

void feedAnimals(Cage<? extends Animal> someCage) {

       for (Animal a : someCage)

       a.feedMe();

       }

       这样一来,我们就可以把每种animal装入到对应独立的cage中,然后依次调用这个方法,如下:

feedAnimals(lionCage);

feedAnimals(butterflyCage);

       或者把所有的animal cage组合起来,然后可以用下面的代码进行代替:

feedAnimals(animalCage);

 

系统分类: 软件开发  |  用户分类: Java学习  |  标签: java 泛型 通配符  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(437) | 回复(0)

发表于 2008/11/12 20:59:27

0

关于投票

原创java连载--泛型(5)

泛型的子类型

       只要两种类型能够相符,我们可以把一种类型的对象赋给另外一种类型的对象。例如,可以把一个Integer赋给一个Object,因为ObjectInteger的父类之一。

    Object someObject = new Object();

    Integer someInteger = new Integer(10);

    someObject = someInteger; // OK

       在面向对象的编程中,IntegerObject之间的这种关系叫做 ”is a”,也就是说在本质上是相同的,Integer是一种Object,所以才允许上面的这种赋值。由于Integer也是Number的一种,所以下面的赋值也是有效的:

    public void someMethod(Number n){

        // method body omitted

    }

 

    someMethod(new Integer(10)); // OK

    someMethod(new Double(10.1)); // OK

       对于泛型来讲,上面的讨论的这种关系也是适用的。在一个泛型调用中,我们可以传递Number作为它的参数,但是在后续调用add方法的时候,我们使用和Number相符的类型作为其参数:

    Box<Number> box = new Box<Number>();

    box.add(new Integer(10)); // OK

    box.add(new Double(10.1)); // OK

 

       我们现在考虑下面的方法:

    public void boxTest(Box<Number> n){

        // method body omitted

    }

       上面这个方法能够接受的参数是什么呢?实际上,我们可以看出它的参数是Box<Number>。那么我们能不能传递Box<Integer>或者Box<Double>作为它的参数呢?不能,原因就是Box<Integer>Box<Double>不是Box<Number>的子类型。

系统分类: 软件开发  |  用户分类: Java学习  |  标签: java 泛型 子类型  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(676) | 回复(0)

发表于 2008/11/10 22:20:26

0

关于投票

原创java连载--泛型(4)

受限的类型参数(Bounded Type Parameters)

       有时候,我们要限制传递给类型参数的具体参数。例如,对数进行操作的方法就只能接受Number或者其子类的对象作为改方法的参数,而不能接受其他类型的参数。这也就是要对参数类型进行限制的原因。

       在申明一个类型参数的时候,如果在类型参数名后跟着extends关键字,而extends关键字后面又跟着类型参数的上限(upper bound),例如这个上限可以是个数类Number,那么这个被申明的类型参数就是一个受限的参数类型。需要注意的是,这里的extends关键字可以是普通意义上类“继承”的意思,也可以是接口上“实现”的意思。

/**

 * This version introduces a bounded type parameter.

 */

public class Box<T> {

    private T t;         

    public void add(T t) {

        this.t = t;

    }

    public T get() {

        return t;

    }

    public <U extends Number> void inspect(U u){

        System.out.println("T: " + t.getClass().getName());

        System.out.println("U: " + u.getClass().getName());

    }

    public static void main(String[] args) {

        Box<Integer> integerBox = new Box<Integer>();

        integerBox.add(new Integer(10));

        integerBox.inspect("some text"); // error: this is still String!

    }

}

       如上代码所示,U就是一个受限的类型参数,只能向其传递Number类或者Number子类的参数。如果对上面的代码进行编译的时候,就会报错,原因就是调用inspect方法的时候向其传递了一个String的参数"some text"

       在定义受限类型参数的时候,如果还想要实现接口的话,就可以将要实现的接口使用&字符连接在类型参数上限(upper bound)的后面,如下所示:

       <U extends Number & MyInterface>

       要想实现多个接口的话,就用&依次将要实现的接口跟在后面就可以了,如下:

       <U extends Number & MyInterface1 & MyInterface2 … >

系统分类: 软件开发  |  用户分类: Java学习  |  标签: java 受限泛型  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(582) | 回复(0)

发表于 2008/11/9 21:49:39

0

关于投票

原创java连载--泛型(3)

泛型方法和构造器

       如果在申明方法或者构造器的时候使用类型参数的话,就可以定义泛型方法和泛型构造器。这和定义一个普通的泛型基本上无二样,除了类型参数的作用范围只是在定义它的方法或者构造器之中。

/**

 * This version introduces a generic method.

 */

public class Box<T> {

    private T t;         

    public void add(T t) {

        this.t = t;

    }

    public T get() {

        return t;

    }

 

    public <U> void inspect(U u){

        System.out.println("T: " + t.getClass().getName());

        System.out.println("U: " + u.getClass().getName());

    }

    public static void main(String[] args) {

        Box<Integer> integerBox = new Box<Integer>();

        integerBox.add(new Integer(10));

        integerBox.inspect("some text");

    }

}

       在上面的例子中,我们定义了一个泛型方法inspect,而该泛型方法定义了一个类型参数U。这个方法接收输入的参数并将其类型输出,为了比较,它也将T的类型输出。这个类中还有一个main方法使得它可以直接运行,而且输出如下:

       T: java.lang.Integer

       U:java.lang.String

       通过输入不同的参数,输出也会跟着相应的变化。

系统分类: 软件开发  |  用户分类: Java学习  |  标签: java 泛型 generic  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(614) | 回复(0)

发表于 2008/11/8 19:13:59

1

关于投票

原创java连载--泛型(2)

我们可以通过将"public class Box" 修改为 "public class Box<T>"而定义一个泛型,在这个定义中,使用了一个类型变量(type variable) T,而且T能够在Box类之内的任何地方被使用。这中定义的方法其实并不复杂,并且在接口(interface)中也被使用。实际上,T可以看作是一种特殊的数据类型,它的值就是我们要传递给它的参数,参数的类型可以是类,也可以是接口,或者其他类型的变量,但是却不能是原始类型(primitive)的数据。

/**

 * Generic version of the Box class.

 */

public class Box<T> {

    private T t; // T stands for "Type"         

    public void add(T t) {

        this.t = t;

    }

    public T get() {

        return t;

       }

              }

       如上所示,我们用T代替了其中所有的Object。为了在我们的代码中能够使用泛型类,我们必须要用具体的类型比如Integer来代替T,这也叫做泛型调用(generic type invocation)

实际上,泛型调用和普通的方法调用非常相似,所不同的是方法调用时传递的是参数,而泛型调用是传递的是一种具体的类型参数,比如Integer。泛型调用也叫做参数化类型(parametersized type)

       为了实例化使用泛型技术定义的类,和通常一样要使用new关键字,如下所示:

              integerBox = new Box<Integer>();

       或者如果写的更加全面的话可以用下面的语句:

           Box<Integer> integerBox = new Box<Integer>();

       一旦integerBox被实例话了,我们就可以使用它的get方法而无需进行参数的转换。而且如果试图想加一个类型不符的参数到box,编译器就会报错。

       我们一定要记住,类型变量并不是真正的数据类型本身,上面的例子中,在文件中是找不到T.java或者T.class的,而且T也不是类名Box的一部分。实际上在编译的时候,所有和泛型有关的信息都会被去掉,从而最后只有Box.class文件,这会在后面进一步讨论。

       另外还要注意的是泛型可以多个类型参数,但是每个类型参数在所定义的类或者接口中不能重复。例如Box<T, T>则是有问题的,而Box<T, U>则是可以使用的。

       按照惯例,类型参数一般使用单个大写的字母表示。这样就可以普通变量的命名形成了明显的对比。如果不按照此惯例,就很难区分类型参数名和普通的类名或者接口名。

       通常使用的类型参数如下:

       E 元素(Element)

       K 关键字(Key)

       N (Number)

       T 类型(Type)

       V (Value)

       SUV 2个,第3个,第4个类型。

系统分类: 软件开发  |  用户分类: Java学习  |  标签: java 泛型 generic  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(440) | 回复(0)

发表于 2008/11/7 22:43:21

0

关于投票

原创java连载--泛型(1)

从今天开始学习Java的泛型(Generics),它能够帮助程序员书写更加可靠的程序和软件。

1.       介绍

通常,缺陷严重影响着大型程序和软件的使用。通过周密的设计、编码和测试,或许可以减少一些缺陷,但是缺陷对程序来讲简直就是无孔不入,特别是在要引入一些新的特性或者程序越来越大越来越复杂的时候。值得我们高兴的是有些缺陷能很容易被发现,这给我的工作带来了极大的方便。例如编译时的缺陷能够立刻告诉我们某个地方有错误,我们也可以通过编译输出的错误信息判断和找出错误所在,并且修改它。运行时的缺陷就没有那么好对付了,因为它们隐藏的很深,喜欢和我们玩捉秘藏,往往很难被发现,有时即使在某些时候我们发现了它们,要找到产生它们原因的道路还很曲折。泛型(Generics)能够帮助我们在编译程序的时候就发现更多的缺陷。

下面代码定义了一个Box类,有两个方法add()get()

       public class Box {

        private Object object;

        public void add(Object object) {

            this.object = object;

        }

        public Object get() {

            return object;

        }

       }

因为它的方法的参数是Object,所以我们可以随意传递任何参数到add()方法中,即使我们要想传递的不是原始类型也没有关系。然而事实上,我们应该严格限制传递的参数类型,但是目前我们只能做的就是在文档中加以说明或者在代码中添加相应的注释,编译器却对参数类型所作限制是不得而知的。

public class BoxDemo {

    public static void main(String[] args) {

        // ONLY place Integer objects into this box!

        Box integerBox = new Box();

 

        // Imagine this is one part of a large application

        // modified by one programmer.

        integerBox.add("10"); // note how the type is now String

 

        // ... and this is another, perhaps written

        // by a different programmer

        Integer someInteger = (Integer)integerBox.get();

        System.out.println(someInteger);

              }

                     }

       BoxDemo中,没有将字符串10”转换成整数对象,很明显是个缺陷。但是在编译的时候并不能发现这个缺陷,从而造成在运行的时候出现错误。如果在定义Box类的时候能够使用泛型,那么这个错误在编译的时候就会被发现。

系统分类: 软件开发  |  用户分类: Java学习  |  标签: 泛型 generics  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(353) | 回复(0)

发表于 2008/11/6 22:35:50

0

关于投票

原创java连载--数和字符串

上个月中旬开始学习java,不是因为我要用他。只是觉得它是一种优秀的面向对象的高级编程语言,而且用它写的程序可以做到跨平台使用,所以就想学习一下,扩展一下自己的思维。为了能和大家讨论和分享我的学习体会,所以将我的笔记发上来,前一段时间的学习笔记都是用英文做的,就不发了,从今天开始。我学习的主要资料来源是http://java.sun.com/docs/books/tutorial/

 

       1. 数和字符串之间可以相互转换:

       通常可以使用数类的parserXXXX()或者valueOf()可以将字符串转换成对应的数。

有时候,我们需要将数转换成相应的字符串,这个时候可以用以下几种方式:把要转换的数值和一个空的字符串合并;也可以使用字符串类的valueOf()方法;数类还提供了一种方法toString,使用该方法可以很容易的将数值转换成字符串。

 

2. 对字符串种字符的操作:

字符串类有一系列的方法可以检查字符串的内容,寻找字符串种的某个字符或者子字符串,修改字符的大小写,以及其他的工作。

获取字符串种某个特定位置字符的方法是charAt(),例如str.charAt(2)就是获取字符串str中第2个索引出的字符,需要注意的字符串的索引是从0开始,最后一个所以就是字符串的长度减一,这样字符串中最后一个字符就是str.charAt(str.length() - 1)

在某些时候,我们可能需要获取的是字符串中一段连续的字符而不是一个字符。我们把字符串中一段连续的字符成为这个字符串的子字符串,而且我们可以使用substring方法可以获取字符串中的子字符串。例如str.substring(2)获得的子字符串就是从str中第2个字符开始一直到str结尾,而str.substring(2,4)获得的字符串是从str的第2个字符开始到第4个字符结束。

转换大小写的方法是toLowerCase()toUpperCase(),顾名思义,不作赘述。

trim()这个方法能返回一个新的字符串,这个新的字符串去掉了原来字符串开始和结尾出的空格。

搜索是很有必要的,无论是搜索字符还是子字符串。基本的方法有两个,一个是indexOf(),另外一个是lastIndexOf()。如下表所示:

方法

描述

int indexOf(int ch)

int lastIndexOf(int ch)

返回字符串中第一个(最后一个)出现指定字符的索引。

int indexOf(int ch, int fromIndex)

int lastIndexOf(int ch, int fromIndex)

从指定的索引开始从前往后(从后往前)开始搜索,返回第一个(最后一个)出现指定字符的索引。

int indexOf(String str)

int lastIndexOf(String str)

返回字符串中第一个(最后一个)出现指定字符串的索引。

int indexOf(String str, int fromIndex)

int lastIndexOf(String str, int fromIndex)

从指定的索引开始从前往后(从后往前)开始搜索,返回第一个(最后一个)出现指定字符串的索引。

搜索虽然有时很管用,但是我们有时候,我们不管要找到,而且还要更换。此时,replace()方法给我们提供了了有力的帮助,使用replace相关的方法,可以很轻松的完成指定字符或者子字符串的替换。如下表所示:

方法

描述

String replace(char oldChar, char newChar)

返回一个新的字符串,该字符串使用newChar替换了原字符串中所有的oldChar

String replace(CharSequence target,CharSequence replacement)

replacement替换字符串中所有的target字符序列。

和正则表达式相关的字符串的操作方法先不在这里学习,等学完正则表达式之后再回来进行学习。

 

3.字符串的比较

字符串类为我们提供了很多字符串之间进行比较或者字符串与字符串的一部分进行比较的方法。掌握这些方法可以帮助我们快速的完成字符串比较的相关工作。具体介绍如下表所示,同样下面也不包括和正则表达式有关的比较方法。

方法

描述

boolean endsWith(String suffix)

boolean startsWith(String prefix)

如果字符串以prefix开始或者以suffix结束就返回true

boolean startsWith(String prefix, int offset)

offset开始,如果字符串按照prefix开始则返回true.

int compareTo(String anotherString)

顺序比较两个字符串,返回值有大于0或等于0或小于0

int compareToIgnoreCase(String anotherString)

顺序比较两个字符串,忽略大小写,返回值有大于0或等于0或小于0

boolean equal(Object anObject)

当且仅当anObject是一个字符串对象并且和当前对象具有相同的字符序列时返回true.

boolean equalIgnoreCase(Object anObject)

当且仅当anObject是一个字符串对象并且和当前对象具有相同的字符序列时返回true,比较是忽略大小写.

boolean regionMatches(int toffset, String other, int ooffset, int len)

比较两个字符串中是否有相同的区域,区域的长度是len,当前字符串的从toffset开始,而另一个字符串从ooffset开始。

boolean regionMatches(boolean ignoreCase,  int toffset, String other, int ooffset, int len)

比较两个字符串中是否有相同的区域,区域的长度是len,当前字符串的从toffset开始,而另一个字符串从ooffset开始。弱ignoreCasetrue,则比较时忽略大小写,否则不忽略。

 

4. StringBuilder

StringBuilderString不同之处在于StringBuilder可以被修改,而String不能被修改。StringBuilder就像是一个可变长度的数组,并且其长度和其中的内容都可以通过调用它的方法加以修改。大多数情况下,我们应当使用String而不是StringBuilder,除非使用StringBuilder能带来明显的好处,比如使源码更加简单,能获得更高的效率,例如要将很多String字符串合并起来,则使用StringBuilder比较好。

关于length()capacity()StringBuilderlengthString是一样,都是字符串的长度,也就是字符串中包含字符的个数。而capacityStringBuilder独有的,其含义是为字符串非配的存储空间的大小,并且能够根据需要自动扩展大小。和lengthcapacity有关的两个方法是setLength()ensureCapacity

StringBuilder主要的操作有append,insert,delete,reserve,replace,toString等,详见下表:

方法

描述

StringBuilder append(boolean b)
StringBuilder append(char c)
StringBuilder append(char[] str)
StringBuilder append(char[] str, int offset, int len)
StringBuilder append(double d)
StringBuilder append(float f)
StringBuilder append(int i)
StringBuilder append(long lng)
StringBuilder append(Object obj)
StringBuilder append(String s)

将参数转换成String字符串并附加到StringBuilder后面。

StringBuilder delete(int start, int end)
StringBuilder deleteCharAt(int index)

删除StringBuilder中指定的字符或字符串。

StringBuilder insert(int offset, boolean b)
StringBuilder insert(int offset, char c)
StringBuilder insert(int offset, char[] str)
StringBuilder insert(int index, char[] str, int offset, int len)
StringBuilder insert(int offset, double d)
StringBuilder insert(int offset, float f)
StringBuilder insert(int offset, int i)
StringBuilder insert(int offset, long lng)
StringBuilder insert(int offset, Object obj)
StringBuilder insert(int offset, String s)

将第二个参数转换为String字符串并插入到第一个参数指定的位置处。

StringBuilder replace(int start, int end, String s)
void setCharAt(int index, char c)

替换StringBuilder中指定的字符或者字符串。

StringBuilder reverse()

颠倒StringBuilder中的字符的顺序。

String toString()

返回一个包含StringBuiler中字符序列的String字符串。

一个小技巧:可以使用toString()先将StringBuilder对象转换成String对象,这样就可以使用String对象的所有方法,处理完之后再使用StringBuilder(String str)构造器将String对象转换为StringBuilder对象。

总结:NumberString之间可以相互转换,String类有很多实用的方法,主要包括查找,比较,替换等,StringBuilder提供了一种可修改的变长字符串。

系统分类: 自由话题  |  用户分类: Java学习  |  标签: Number String StringBuilder  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(468) | 回复(0)

Total , Page /