Java语言基础特性—第一部分(下)

来源:互联网 发布:javascript var 数组 编辑:程序博客网 时间:2024/05/17 08:36
本文由 ImportNew - 陈 晓舜 翻译自 javaworld。欢迎加入Java小组。转载请参见文章末尾的要求。

你可以通过指定extends后接类型名称来提供通配符的上界。同样的,你可以通过指定super后接类型名称来提供通配符的下界。这些限定限制了可以作为实际类型参数传入的类型。

在例子中,你可以把? extends String理解为任何String或其子类的实际类型参数。同样的,你可以把? super String理解为任何String或其父类的实际类型参数。因为String是final的,这意味着它不能被继承,只有源列表为String对象,目标列表为String或Object对象能够传入作为参数,这样用处不大。

你可以使用泛型方法来完全解决这个问题,它是一个有类型实现参数的类或接口方法。泛型方法支持下面的语法:

1
2
<formalTypeParameterList> returnType
identifier(parameterList)

泛型方法的形参列表在它的返回类型之前。它包含类型参数和可选的上界。类型参数可以作为返回类型使用,并且可以出现在参数列表中。

清单9展示了怎么定义和调用泛型copy()方法

Listing 9. GenDemo.java (version 5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

importjava.util.ArrayList;

importjava.util.List;

 

publicclass GenDemo

{

   publicstatic void main(String[] args)

   {

      List<Integer>grades = newArrayList<Integer>();

      Integer[]gradeValues =

      {

         newInteger(96),

         newInteger(95),

         newInteger(27),

         newInteger(100),

         newInteger(43),

         newInteger(68)

      };

      for(inti= 0; i < gradeValues.length; i++)

         grades.add(gradeValues[i]);

      List<Integer>failedGrades = newArrayList<Integer>();

      copy(grades,failedGrades, newFilter<Integer>()

                                 {

                                    publicboolean accept(Integer grade)

                                    {

                                       returngrade.intValue()<= 50;

                                    }

                                 });

      for(inti= 0; i < failedGrades.size(); i++)

         System.out.println(failedGrades.get(i));

   }

 

   static<T>voidcopy(List<T> src, List<T> dest,

Filter<T> filter)

   {

      for(inti= 0; i < src.size(); i++)

         if(filter.accept(src.get(i)))

            dest.add(src.get(i));

   }

}

 

interfaceFilter<T>

{

   booleanaccept(T o);

}

清单9中我定义了一个<T> void copy(List<T> src, List<T> dest, Filter<T> filter)泛型方法。编译器注意到srcdestfilter参数的类型都包含类型参数T。这意味着在方法调用中必须传入同样的实际类型参数,而编译器会在调用中获取参数。

如果你编译清单9(javac GenDemo.java)并运行程序(java GenDemo),你应该可以看到下面的输出:

1
2
27
43

Java语言中关于泛型最有争议的是什么?

虽然泛型本身并不具争议,但它在Java语言中的特殊实现却是。泛型是作为消除转换的语法糖的编译时特性来实现的。编译器会在编译源码后丢弃泛型类型或泛型的形参类型列表。这个“丢弃”行为称为擦除(erasure)。其他在泛型中关于擦除的例子包含:在代码类型不正确时,插入时可以自动转换为合适的类型;通过上界(例如Object)来替换类型参数。

更多关于泛型的讨论

泛型不只因为擦除而备受争议。看一下StackOverflow.com的“为什么我们抱怨Java关于泛型的实现很糟糕”主题的讨论,包含了通配符很难理解和事实上泛型并不直接值类型(例如,你不能指定List<int>)。

使用擦除会有下面的几个限制:

  • instanceof并不能用于参数化类型,只有一种情况是例外的。这个例外就是无界的通配符。例如,你不能指定Set<Shape> shapes = null; if (shapes instanceof ArrayList<Shape>){}。相反,你需要把对instanceof表达式修改为shapes instanceof ArrayList<?>,这种就是无界的通配符。或者,你可以指定shapes instanceof ArrayList,这使用的是原生类型(通常也是推荐使用的做法)。

  • 编译器把泛型代码转换为非泛型代码,并保存在class文件中。一些开发人员指出擦除会使得你不能通过反射取得泛型信息,因为它们并不保存在class文件中。开发人员Jakob Jenkov在“Java 反射:泛型”中指出一些泛型信息会被保存在class文件中的情况,并且这些信息可以通过反射来访问。

  • 你不能在创建数组的表达式中使用类型参数;例如,elements = new E[size];。如果你这样做,编译器会报告泛型数组创建错误信息。

鉴于擦除的限制,你会奇怪为什么泛型要通过擦除来实现。原因很简单:Java编译器被重构来使用擦除,因此泛型代码可以跟那些非泛型的遗留代码进行交互。没有这个向后兼容性,遗留代码在支持泛型的Java编译器上编译时将会报错。

第一部分总结

Java语言已经添加了许多新特性。在这篇文章中,我展示了怎么使用断言来增强你在代码正确性上的信心,和如何使用泛型来消除ClassCastException。通过使用断言和泛型,你可以编写更可靠的代码,并且使你的代码在运行时的错误降到最低,当然,也减少面对生气的客户时的头痛了。

Java 5 是Java平台历史上的一个重大发布,虽然泛型比其他特性都更具争议,但它却比其他都更加重要。我的下篇文章将会介绍另外7个在Java5时加入的必要的特性:类型安全的枚举,注解,自动装箱和拆箱,加强的循环,静态引入,可变参数,协变返回类型。在那之前,下载这篇文章的源代码,它包含了更多的关于断言和泛型的提示和例子。

Jeff Friesen是一个自由职业导师和侧重Java和Android的软件开发人员。除了为Apress写Java和Android书籍,Jeff为JavaWorld,informIT,Java.net,DevSource和SitePoint写了大量的关于Java和其他技术的文章。你可以通过他在TutorTutor.ca的网站联系到他

了解更多主题相关

下载文章的源代码
阅读Angelika Langer的Java Generics FAQs,那里有着大量的关于Java语言泛型的信息和观点。

对于想学习Java语言和它的备受争议的特性的,Langer的文章理解闭包的争论(2008.6 JavaWorld)对比了Java 7语言中的三个关于添加闭包和lambda表达式的初始提议。

可以查看“Java反射:泛型”(Jakob Jenkov, Jenkov.com)关于泛型反射和某些情况下可以在运行时获取泛型信息的讨论。
Java无痛并发编程,第一部(2013.6):介绍了Executor框架,同步类型和Java并发集合包。

Java无痛并发编程,第二部(2013.8):介绍了锁,原子变量和fork/join操作,还附加了Java8中关于java.util.concurrent的修改概述。
跟上Java Date和Time API(2013.4):介绍了Java8的JSR310:Date 和Time API,并且展示了你最有可能使用的java.time系列类的使用。
JavaWorld中更多关于Java集合框架的文章:
Java集合框架从零开始(1998.11 Dan Becker):这篇文章介绍了集合刚引入java时的历史。
Java集合中的省时习惯(2013.9 Java Q&A blog):展望未来,Jeff Friesen回答一些当前使用Java集合的常见问题。

0 0
原创粉丝点击