我相信很多人跟我一样还未学习过范型的概念就开始使用范型的实例,最典型的就是集合框架。为了进一步深入了解范型,这一次通过几个简单的例子来说明范型的注意事项。
一.没有范型的世界
所有的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; } }
范型为我们的编码过程做出了巨大的贡献,提供了更加严格的类型检查,并且在取得元素时无需再进行类型转换,真乃编码之一大利器也。
相关推荐
C++的范型(Template)是其强大的特性之一,它允许开发者创建泛化的类和函数,这些实体能够在编译时根据不同的类型参数实例化。这使得程序员能够编写出不依赖具体数据类型的通用代码,提升了代码的灵活性和可维护性...
4. 函数对象(Function Objects)或谓之适配器(Adapters):包括比较函数对象(如less、greater)和操作函数对象(如plus、minus),它们封装了操作行为,使得算法可以更加灵活和多样化。 学习范型编程与STL对于...
本文提出的基于消息传递范型和客户机/服务器(Client/Server,简称C/S)范型双范型的主数据管理机制,能够有效解决MDM面临的问题。 消息传递范型是计算机科学中的一个基本概念,用于描述进程间通信的方式。在这范型...
一致性是指系统中所有节点在任何时刻都能够就系统状态达成一致,是分布式系统设计的基础之一。可靠性强调系统能够持续提供服务,即使在部分组件失效的情况下也能正常工作。容错性是指系统能够在面临错误时,继续正常...
Java 范型Java 范型
第7章综合考虑前几章的内容,介绍了结构复杂性不同的设计问题的分类,以及可作为基于领域分析和多范型设计技术基础的高级活动集合。第8章研究结构复杂的设计,并介绍如何组合各种范型。第9章补充说明了流行的设计...
《分布式系统原理与范型》是分布式系统中的经典教材,全书分为两部分:原理和范型。第一部分详细讨论了分布式系统的原理、概念和技术,其中包括通信、进程、命名、同步、一致性和复制、容错以及安全。第二部分给出了...
C++ 设计新思维 范型编程与设计模式之应用 pdf
分布式系统原理与范型是IT领域中至关重要的一个主题,特别是在云计算、大数据处理以及现代互联网服务的背景下,理解和掌握分布式系统的概念与实践至关重要。本书作为第二版,深度探讨了这一领域的核心理论和常见模式...
2. **CAP理论**:分布式系统设计的核心原则之一是CAP定理,它指出一个分布式系统不能同时满足一致性、可用性和分区容错性这三个特性。理解和权衡这三者在实际系统设计中的重要性是至关重要的。 3. **Paxos算法**:...
综上所述,分布式系统原理与范型这本著作深入地介绍了分布式系统的基础知识、基本原理以及设计范例,旨在帮助读者全面理解和掌握分布式系统的理论和技术。通过学习和研究这些内容,读者将能够更好地设计和实现高效、...
C++多范型设计.pdfC++多范型设计.pdfC++多范型设计.pdf
《C++设计新思维:范型编程与设计模式之应用》一书深入剖析了范型编程这一核心概念,以及在C++语言中的具体应用。范型编程,也称为泛型编程,是C++中的一项重要特性,它允许程序员编写不依赖特定数据类型的代码,...
2. **网络基础**:了解网络协议、IP地址、TCP/IP模型,以及它们在分布式系统中的作用。 3. **进程间通信**:学习不同类型的进程间通信机制,如RPC(远程过程调用)、消息队列、RESTful API等,以及它们的优缺点。 ...
第2~9章讨论的是分布式系统的的原理、概念和技术,包括通信、进程、命名、同步化、一致性和复制、容错性以及安全性等,而分布式应用的开发方法(即范型)在第10~13章中进行了讨论。但是,与前一版不同的是,我们...
分布式系统-原理与范型(第2版) 分布式系统-原理与范型(第2版) 分布式系统-原理与范型(第2版) 分布式系统-原理与范型(第2版)
自Java 1.5发布以来,范型成为了Java语言的重要特性之一,极大地提升了代码的类型安全性与复用性。在早期版本的Java中,如Java 1.4及以前,集合框架(如`ArrayList`)只能存储`Object`类型的数据,这导致了类型信息...
分布式系统原理与范型中文版 经典分布式理论书籍