我相信很多人跟我一样还未学习过范型的概念就开始使用范型的实例,最典型的就是集合框架。为了进一步深入了解范型,这一次通过几个简单的例子来说明范型的注意事项。
一.没有范型的世界
所有的java类都派生自java.lang.Object ,这意味着所有的java对象都可以转换成Object,听起来似乎很美妙,但事实并非如此。举个例子,假设现在需要一伙人去排队,要求只有学生可以参与进来,但是如果对于这个队伍没有条件限定的话,那就意味着我们不想要的一些群体也会进入大军之中,这不利于管理。再如下面一段没有使用范型的代码:
List string=new ArrayList();
string.add("我是第一个元素");
string.add("我是第二个元素");
当要从中获取一个成员时,得到的却是java.lang.Object的一个实例,那么如果我们要使用String类型的对象还要进行强转型,这无疑增加了代码的冗杂度。不过幸好,我们有了范型。
二. 范型类型简介
像方法一样,范型类型也可以接受参数,声明范型类型要使用尖括号将类型变量列表括起来。例如声明一个List对象:
List<E> mylist;
为了将范型类型实例化,要在声明它的时候传递相同的参数列表,例如,为了创建一个使用String的ArrayList,要将String放在一对尖括号中进行传递:
List<String> string=new ArrayList<String>();
不过,java7版本之后,可以在参数化类的构造器中显式的传入参数,上面一段代码可以改为如下的表达方式:
List<String> string=new ArrayList< >();
毫无疑问范型类型也可以指定Object类型,但是范型类型却不可以是java.lang.Throwable的直接或间接子类,因为它在运行时抛出异常,因此无法查看在编译时会抛出什么异常。
下面一段代码就对使用了范型与未使用范型的List进行了比较:
import java.util.ArrayList; import java.util.List; public class GenericListTest { public static void main(String args[]){ //没有使用范型的队列 List stringList=new ArrayList(); stringList.add("元素1"); stringList.add("元素2"); //需要强转型 String s1=(String)stringList.get(0); System.out.println(s1); //使用范型的队列 List<String> list=new ArrayList<String>(); list.add("元素3"); list.add("元素4"); //不需要强转型 String s2=list.get(0); System.out.println(s2); } }
提示:使用范型类型时,是在编译时进行类型检查的。
这里值得关注的地方在于,范型类型本身也是一个类型,它还可以用作类型变量。
如下的一段代码演示:
import java.util.ArrayList; import java.util.List; public class ListOfList { public static void main(String args[]){ List<String> list=new ArrayList<>(); list.add("我是list中的对象"); //list中的元素 System.out.println(list.get(0)); //创建封装了list的List对象 List<List<String>> listOflist=new ArrayList<>(); listOflist.add(list); //获取list当中的元素 String s=listOflist.get(0).get(0); System.out.println(s); } }
范型类型可以接受不止一个类型变量,那就是我们非常熟悉的Map集合,相信大家也已经相当熟悉了吧
三. 使用“?”通配符
前面所提到的,如果声明一个List<aType>,那么List将使用aType的实例,并且可以保存以下任意一种类型:
1. aType的一个实例
2. aType的子类的一个实例,如果aType是类的话
3. 实现aType的类的一个实例,如果aType是接口的话
但是,注意范型类型本身也是一个java类型,如果你不理解的话,下面这段代码将会让你更加了解这句话的含义:
import java.util.ArrayList; import java.util.List; public class AllowedTypeTest { public static void main(String args[]){ List<String> myList=new ArrayList<String>(); doIt(myList); } private static void doIt(List<Object> l){ } }
编译产生了错误,按理说,String是Object的一个子类,但是进行了范型类型封装之后List<String>就不再是List<Object>的一个实例了。
为了解决这个问题,我们就要用到通配符“?”,如下:
import java.util.ArrayList; import java.util.List; public class WildCardTest { private static void doIt(List<?> l){ for(Object element:l){ System.out.print(element); } } public static void main(String args[]){ //字符型List List<String> stringList=new ArrayList<String>(); stringList.add("Hello"); stringList.add("World"); doIt(stringList); System.out.println(); //整型List List<Integer> intList=new ArrayList<Integer>(); intList.add(100); intList.add(200); doIt(intList); } }
代码中doIt方法里面的List<?>表示的是任意类型的一个List,但要注意的是,在声明或者创建一个范型类型时使用通配符是不合法的,如:
List<?> list=new ArrayList<?>();
编译会出错。
四. 在方法中使用有界通配符
通过上面的描述,你已经知道了可以使用“?”通配符来解决类型匹配的问题,但是我们在实际中碰到的问题并非这么简单。如果我们想定义一个Number实现子类的List,虽然我们可以用通配符来解决List<Integer>与List<Double>类型不匹配的问题,但是无疑的,我们还可以放入很多我们并不需要的实例如List<String>,这样程序的类型安全性根本就没有保障了,那么我们就必须需要一种规则来保证我们所定义的List实例是属于Number的实现子类的,在这里我们就要用到有界通配符。
如下演示:
import java.util.ArrayList; import java.util.List; public class BoundedWildcardTest { public static void main(String args[]){ List<Integer> intList=new ArrayList<>(); intList.add(3); intList.add(30); System.out.println(getAverage(intList)); List<Double> doubleList=new ArrayList<>(); doubleList.add(3.0); doubleList.add(30.0); System.out.println(getAverage(doubleList)); } private static double getAverage(List<? extends Number> list){ double total=0.0; for(Number number:list){ total+=number.doubleValue(); } return total/list.size(); } }
五.编写范型类型
编写范型类型与其他类型并无太大差异,只是在类中的某处声明需要用到的类型变量列表。下面就是范型类的一个简单示例:
public class MyGeneric<E> { E a; public MyGeneric(E a){ this.a=a; } public E get(){ return a; } public void set(E b){ this.a=b; } }
范型为我们的编码过程做出了巨大的贡献,提供了更加严格的类型检查,并且在取得元素时无需再进行类型转换,真乃编码之一大利器也。
相关推荐
5. Linux操作系统上的操作这部分内容可能涵盖了在Linux环境下进行开发和调试的基础知识,如命令行工具、版本控制系统(如Git)、构建工具(如Makefile)以及系统调用和进程管理。 通过学习《代码阅读方法与实践》,...
对于计算机专业的学生,教学方法应结合基础知识与实际应用,例如引入程序范型、测试、代码优化、函数栈、接口、数据存储等实践性强的内容,以提高学生的实战能力。同时,关注不同层次的学生,对教材实例进行详细讲解...
- **背景分析:** 面对职业教育中倡导的“能力本位”教学法,《抗震结构》作为建筑工程技术专业的重要专业课程之一,其传统的“学科本位”教学模式显得过于侧重于理论知识的传授,缺乏实践性和针对性,这与现代职业...
C++ 面向对象程序设计是计算机相关专业中不可或缺的基础课程,它涵盖了抽象、封装、继承和多态等核心概念,是现代程序设计的主要范型。然而,由于课程内容的深度和广度,以及语法的复杂性,传统的教学方式往往难以...
第三版在原有的基础上进一步强化了对C++新特性的介绍,重点讲解了如何编写高效且可维护的C++代码。下面是根据提供的文件内容,总结出的一些知识点。 首先,C++是一个多范型编程语言,它支持过程化、面向对象、函数...
2. **强化实践能力**:通过一系列的专业课程和实训活动,确保学生能够在毕业后顺利进入IT领域工作,胜任软件程序设计、软件项目开发、计算机选购与安装、计算机组网与Web开发等相关岗位的工作。 #### 二、职业面向...