Java泛型编程指南
- 格式:doc
- 大小:206.50 KB
- 文档页数:27
Java中的泛型编程技巧Java 中的泛型编程技巧随着软件系统规模的不断扩大,代码的维护成本也愈来愈高。
泛型编程(Generics Programming)正是为了解决这个问题而生的。
Java 作为一门比较成熟的编程语言,自 J2SE 5.0 版本起加入了泛型机制,使得 Java 的类型安全性得到进一步提高。
本文将介绍一些Java 中的泛型编程技巧,帮助大家编写更加清晰、简洁、安全、可复用的代码。
一、泛型基础1.1 泛型类泛型类(Generic Class)是指可以在类定义时指定类型参数的类。
泛型类可以包含普通成员变量和普通成员方法。
类型参数可以是任意类型,包括基本数据类型和引用类型。
例如:```public class MyGenericClass<T> {private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;}}```上面的代码中,T 是一个类型参数,可以被任意类型替代。
在实例化泛型类时会指定实际的类型参数,如下所示:```MyGenericClass<String> generic = new MyGenericClass<>();generic.setValue("Hello, World!");String value = generic.getValue();```1.2 泛型接口泛型接口(Generic Interface)与泛型类类似,可以在接口定义时指定类型参数。
泛型接口可以被实现为泛型类、非泛型类以及其他泛型接口。
例如:```public interface MyGenericInterface<T> {void setValue(T value);T getValue();}```可以将泛型接口用于实现非泛型类:```public class MyGenericClass<T> implements MyGenericInterface<T> {private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;}}```也可以将泛型接口用于实现其他泛型接口:```public interface MyGenericInterface2<U> extends MyGenericInterface<U> {void setOtherValue(U value);U getOtherValue();}```1.3 泛型方法泛型方法(Generic Method)是指可以在方法定义时指定类型参数的方法。
java 泛型的三种定义与使用方法Java中的泛型主要有三种定义方式,分别是:类型参数、类型参数化类和类型参数化方法。
下面是它们的定义和使用方法:1. 类型参数:类型参数是泛型的主要形式,它允许我们在定义类、接口或方法时指定一个或多个类型参数。
这些类型参数在类、接口或方法的实现中被用作类型占位符,以便在运行时确定实际类型。
定义类型参数的方法是在类、接口或方法的名称后面加上尖括号<>,并在其中指定一个或多个类型参数。
例如:```javapublic class Box<T> {private T content;public Box(T content) {= content;}public T getContent() {return content;}}```在上面的例子中,我们定义了一个名为Box的泛型类,它有一个类型参数T。
我们可以使用任何类型来实例化Box对象,例如Box<Integer>、Box<String>等。
2. 类型参数化类:类型参数化类是将一个类作为泛型参数。
这种定义方式主要用于集合框架中的类,例如List、Set、Map等。
定义类型参数化类的方法是在类名后面加上尖括号<>,并在其中指定一个或多个类型参数。
例如:```javaList<String> list = new ArrayList<>();Set<Integer> set = new HashSet<>();Map<String, Integer> map = new HashMap<>();在上面的例子中,我们定义了三个类型参数化类:List、Set和Map,并使用它们创建了三个不同类型的对象。
这些对象在运行时会自动处理实际类型的匹配。
3. 类型参数化方法:类型参数化方法是在方法中使用泛型。
java 方法的泛型Java方法的泛型泛型(Generics)是Java语言中一个强大的特性,它允许我们在编译时期检测类型的一致性,并且提供了更加灵活和安全的代码重用方式。
泛型在Java 5中引入,成为Java语言的一大亮点。
在本文中,我将介绍Java方法中的泛型的使用方法和注意事项。
一、泛型方法的定义和语法泛型方法是一种在方法中使用泛型类型参数的方法。
在方法的返回类型之前使用尖括号,尖括号中指定泛型类型参数。
例如,下面是一个简单的泛型方法的定义:```javapublic <T> void printArray(T[] arr) {for (T element : arr) {System.out.print(element + " ");}System.out.println();}```在上面的例子中,泛型方法printArray使用了一个类型参数T,它表示任意类型。
方法的参数arr是一个泛型数组,类型为T[]。
在方法的实现中,我们可以像操作普通数组一样遍历和处理泛型数组。
二、调用泛型方法调用泛型方法时,可以明确指定泛型类型参数,也可以根据方法参数的类型推断出泛型类型参数。
例如,下面是两种调用泛型方法的方式:```javaString[] strArr = {"Java", "Python", "C++"};printArray(strArr); // 调用泛型方法,类型参数自动推断为StringInteger[] intArr = {1, 2, 3, 4, 5};this.<Integer>printArray(intArr); // 显式指定泛型类型参数为Integer```从上面的例子中可以看出,我们既可以根据参数类型推断出泛型类型参数,也可以显式指定泛型类型参数。
这使得泛型方法在使用时非常灵活。
Java 泛型详解与范例⽬录⼀、泛型的使⽤⼆、泛型类的定义-类型边界三、类型擦除四、泛型类的使⽤-通配符五、泛型⽅法六、泛型的限制⼀、泛型的使⽤前⾯我们学集合的时候,简单的说过泛型的使⽤。
如下:1ArrayList<Integer> list = new ArrayList<>();那么使⽤是这样的简单,该注意什么?尖括号⾥的类型,只能写引⽤类型基础数据类型的话,就需要写相应的包装类型泛型只是编译时期的⼀种机制,在运⾏时是没有泛型的概念的。
⼆、泛型类的定义-类型边界泛型还有⼀个点就是:泛型的上界。
(类型形参 extends 类型边界)有如下代码:1234public class Algorithm<T extends Comparable<T>> { public T findMax(T[] array) { }}以上代码中,⽅法的作⽤就是传递⼀个数组进去,要求返回这个数组中的最⼤值。
这个时候问题就来了,泛型是T 类型,当调⽤这个⽅法的时候,传递过去的参数类型不⼀定就是简单数据类型啊。
那么这个时候该怎么进⾏判断⼤⼩呢此时这样的泛型写法的作⽤就是:T extends Comparable, 就叫做泛型的上界,当传递参数类型的时候,必须传递过去的参数类型必须是实现了Comparable 接⼝的类型才可以。
换句话说,传递过去的类型必须是可以进⾏⽐较的。
当我们⾃⼰定义的⼀个类Node ,然后实现Comparable 接⼝,就能调⽤如上的⽅法。
切记,这样写的泛型,传递过去的参数类型,必须是实现了Comparable 接⼝的,当然也可以传递Comparable 接⼝本⾝。
三、类型擦除类型擦除值得是:代码编译后,会将泛型T ,全部擦除为Object 类型。
如下代码:上⾯这⼀⾏代码,虽然此时写的是Integer 类型的,但是在编译之后,JVM 会⾃动地将Integer 擦除为Object 。
Java语言泛型编程最佳实践讲解泛型在Java语言中是一项非常重要的特性,它提供了类型安全和重用性,使得代码更具可读性和可维护性。
本文将介绍Java语言泛型编程的最佳实践,帮助读者更好地理解和运用泛型。
一、为什么使用泛型?使用泛型的主要目的是增加代码的可读性和类型安全性。
通过泛型,我们可以在编译期间检查类型的正确性,并在运行时避免类型转换错误。
泛型还可以提供代码重用性,避免代码冗余。
因此,使用泛型可以帮助我们编写更优雅、更健壮的代码。
二、泛型的基本用法1. 类型参数在使用泛型时,我们需要使用类型参数来指定具体的类型。
类型参数使用尖括号(<>)表示,放在类名或方法名后面。
例如,下面是一个泛型类的定义:```javapublic class Box<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}}```在上述代码中,`T`是一个类型参数,可以在类的内部表示任意类型。
我们可以通过`T`来定义成员变量、方法参数、返回值等。
2. 类型通配符有时,我们需要在泛型中处理多个不同类型的对象。
这时,可以使用通配符(`?`)来表示任意类型。
例如,我们可以声明一个接收任意类型参数的方法:```javapublic void process(Box<?> box) {// 处理逻辑}```在上述代码中,`Box<?>`表示一个可以处理任意类型参数的`Box`对象。
3. 泛型方法除了在类中使用泛型,我们还可以在方法中使用泛型。
使用泛型方法可以使方法更通用、更具灵活性。
例如,下面是一个泛型方法的定义:```javapublic <T> T getValue(Box<T> box) {return box.getValue();}```在上述代码中,`<T>`表示一个类型参数,它可以在方法内部表示任意类型。
java 泛型创建实体对象并调用方法下载提示:该文档是本店铺精心编制而成的,希望大家下载后,能够帮助大家解决实际问题。
文档下载后可定制修改,请根据实际需要进行调整和使用,谢谢!本店铺为大家提供各种类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by this editor. I hope that after you download it, it can help you solve practical problems. The document can be customized and modified after downloading, please adjust and use it according to actual needs, thank you! In addition, this shop provides you with various types of practical materials, such as educational essays, diary appreciation, sentence excerpts, ancient poems, classic articles, topic composition, work summary, word parsing, copy excerpts, other materials and so on, want to know different data formats and writing methods, please pay attention!Java 泛型:创建实体对象并调用方法1. 引言在现代编程中,泛型是一种强大的工具,它允许我们编写通用的代码,从而提高代码的重用性和可读性。
"Java泛型"是一种在Java编程语言中定义泛型类型的方法。
它是一种用于编写灵活且可重用代码的技术,使得代码能够处理不同的数据类型。
泛型可以让你编写可以处理多种数据类型的代码,而不需要为每种数据类型编写新的代码。
Java泛型的用法主要包括以下步骤:
1. 定义泛型类型:使用尖括号<>来定义泛型类型,并在尖括号中指定泛型类型参数。
例如,List<E>是一个泛型类型,其中E是类型参数。
2. 创建泛型对象:使用泛型类型参数来创建对象。
例如,List<String> list = new ArrayList<String>();创建了一个String类型的List对象。
3. 使用泛型方法:在方法定义中使用泛型类型参数,以便在方法内部使用该参数来操作不同类型的对象。
例如,public static <T> T method(T arg) {return arg;}定义了一个名为method的泛型方法,它接受一个类型为T的参数并返回一个类型为T的对象。
4. 使用通配符:使用通配符来表示未知的泛型类型,以便编写更加灵活的代码。
例如,List<?> list = new ArrayList<String>();表示list 是一个未知类型的List对象,但实际上它是一个String类型的List对象。
总之,Java泛型是一种强大的工具,可以帮助你编写更加灵活、可重用和易于维护的代码。
此系列文章译自SUN的泛型编程指南, 看不懂译文的请看原文/j2se/1.5/pdf/generics-tutorial.pdfJava泛型编程指南一、绪言JDK1.5对JAVA语言进行了做了几个扩展,其中一个就是泛型。
本指南旨在介绍泛型。
如果你熟悉其它语言的构造类似的东西,特别是C++的模板(template),你会很快发现它们之间的相同点及重要的不同点;如果你在其他地方没看到过类似的东西,那反而更好,那样你就可以开始全新的学习,用不着去忘掉那些(对JAVA泛型)容易产生误解的东西。
泛型允许你对类型进行抽象。
最常见的例子是容器类型,比如那些在Collection层次下的类型。
下面是那类例子的典型用法:List myIntList = new LinkedList();//1myIntList.add(new Integer(0));//2Integer x = (Integer) myIntList.iterator().next();//3第3行里的强制类型转换有点烦人,程序通常都知道一个特定的链表(list)里存放的是何种类型的数据,但却一定要进行类型转换。
编译器只能保证迭代器返回的是一个对象,要保证对Integer类型变量的赋值是类型安全的话,必须进行类型转换。
类型转换不但会引起程序的混乱,还可能会导致运行时错误,因为程序员可能会犯错误。
如果程序员可以如实地表达他们的意图,即标记一个只能包含特定数据类型的链表,那会怎么样呢?这就是泛型背后的核心思想。
下面是前面代码的泛型写法:List<Integer> myIntList = new LinkedList<Integer>();//1'myIntList.add(new Integer(0));//2'Integer x = myIntList.iterator().next();//3'请注意变量myIntList的类型声明,它指明了这不仅仅是一个任意的List,还是一个Integer类型的List,写作List<Integer>。
java 泛型的用法【原创实用版】目录1.Java 泛型的概念2.Java 泛型的好处3.Java 泛型的使用方法4.Java 泛型的注意事项正文【1.Java 泛型的概念】Java 泛型是 Java 语言中一种重要的特性,它允许程序员在编译时检查类型安全,从而提高代码的可靠性和可维护性。
泛型是一种抽象概念,它允许我们创建一组具有相同类型约束的类或方法。
在 Java 中,泛型通常用尖括号(<T>)表示,其中 T 是类型参数。
【2.Java 泛型的好处】Java 泛型的主要优点是类型安全和代码重用。
类型安全意味着在编译时检查类型,从而减少运行时的错误。
代码重用指的是使用泛型可以创建一组具有相同类型约束的类或方法,从而减少重复代码。
【3.Java 泛型的使用方法】要使用 Java 泛型,首先需要创建一个泛型类或方法。
以下是一些使用泛型的示例:1) 创建泛型类```javapublic class Box<T> {private T content;public Box() {}public Box(T content) {this.content = content;}public T getContent() {return content;}public void setContent(T content) {this.content = content;}}```2) 创建泛型方法```javapublic class GenericsExample {public static <T> void printList(List<T> list) { for (T item : list) {System.out.println(item);}}public static void main(String[] args) {List<String> stringList = new ArrayList<>();stringList.add("Hello");stringList.add("World");printList(stringList);}}```【4.Java 泛型的注意事项】在使用 Java 泛型时,需要注意以下几点:1) 泛型擦除:Java 泛型在运行时会被擦除,即类型参数会被替换为实际类型。
java泛型语法Java泛型语法是Java编程语言中的一个重要特性,它允许我们编写更加通用和灵活的代码。
通过使用泛型,我们可以在编译时期检测类型错误,并在运行时期避免类型转换异常。
本文将介绍Java泛型的基本语法和使用方法。
一、泛型的定义和作用泛型是Java中的一种参数化类型,它允许我们在定义类、接口和方法时使用类型参数。
通过使用泛型,我们可以将类型作为参数传递给类、接口和方法,从而实现代码的复用和灵活性。
泛型的作用主要有以下几个方面:1. 类型安全:通过使用泛型,我们可以在编译时期检测类型错误,避免类型转换异常。
2. 代码复用:通过定义泛型类、接口和方法,我们可以实现对多种类型的支持,从而提高代码的复用性。
3. 简化代码:通过使用泛型,我们可以减少冗余的类型转换代码,使代码更加简洁。
4. 提高性能:通过使用泛型,我们可以避免使用Object类型,从而减少了装箱和拆箱的开销,提高了代码的执行效率。
二、泛型的基本语法Java中的泛型通过使用尖括号<>来定义类型参数。
在定义类、接口和方法时,我们可以将类型参数放在尖括号中,并在后续的代码中使用该类型参数。
1. 泛型类的定义:```public class GenericClass<T> {private T data;public T getData() {return data;}public void setData(T data) {this.data = data;}}```在上面的代码中,泛型类GenericClass使用了类型参数T。
我们可以在创建GenericClass对象时指定具体的类型,例如:```GenericClass<String> genericString = new GenericClass<>(); genericString.setData("Hello, World!");String data = genericString.getData();```上面的代码中,我们创建了一个GenericClass对象genericString,并指定了类型参数为String。
java泛型详解-绝对是对泛型⽅法讲解最详细的,没有之⼀1. 概述泛型在java中有很重要的地位,在⾯向对象编程及各种设计模式中有⾮常⼴泛的应⽤。
什么是泛型?为什么要使⽤泛型?泛型,即“参数化类型”。
⼀提到参数,最熟悉的就是定义⽅法时有形参,然后调⽤此⽅法时传递实参。
那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于⽅法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使⽤/调⽤时传⼊具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。
也就是说在泛型使⽤过程中,操作的数据类型被指定为⼀个参数,这种参数类型可以⽤在类、接⼝和⽅法中,分别被称为泛型类、泛型接⼝、泛型⽅法。
2. ⼀个栗⼦⼀个被举了⽆数次的例⼦:1 List arrayList = new ArrayList();2 arrayList.add("aaaa");3 arrayList.add(100);45for(int i = 0; i< arrayList.size();i++){6 String item = (String)arrayList.get(i);7 Log.d("泛型测试","item = " + item);8 }毫⽆疑问,程序的运⾏结果会以崩溃结束:1 ng.ClassCastException: ng.Integer cannot be cast to ng.StringArrayList可以存放任意类型,例⼦中添加了⼀个String类型,添加了⼀个Integer类型,再使⽤时都以String的⽅式使⽤,因此程序崩溃了。
为了解决类似这样的问题(在编译阶段就可以解决),泛型应运⽽⽣。
我们将第⼀⾏声明初始化list的代码更改⼀下,编译器会在编译阶段就能够帮我们发现类似这样的问题。
java泛型语法Java泛型是Java语言中的一个重要特性,它允许我们在定义类、接口和方法时使用类型参数,从而增加代码的灵活性和复用性。
本文将对Java泛型语法进行详细介绍,并探讨其使用场景和注意事项。
一、泛型的基本概念泛型(Generics)是Java SE 5中引入的特性,用于在编译时期检查数据类型的安全性。
通过使用泛型,我们可以在类或方法中指定一种或多种类型参数,并在代码中使用这些参数来增加代码的通用性。
泛型的基本语法如下:```class ClassName<T>{// 类体}interface InterfaceName<T>{// 接口体}public class ClassName<T>{// 类体}public <T> void methodName(T t){// 方法体}```在上述语法中,`<T>`表示类型参数,可以是任意标识符。
在类或接口的定义中,我们可以使用类型参数来定义属性、方法和构造函数等。
在方法的定义中,我们可以使用类型参数来定义参数类型、返回值类型和局部变量类型。
二、泛型的使用场景泛型主要用于以下几个方面:1. 容器类:通过使用泛型,我们可以定义各种类型的容器类,如List、Set、Map等。
通过指定类型参数,我们可以限制容器中存储的元素类型,从而提高代码的安全性和可读性。
2. 算法类:通过使用泛型,我们可以定义一些通用的算法类,如排序、查找等。
通过指定类型参数,我们可以提供通用的算法实现,从而减少代码的重复编写。
3. 接口和抽象类:通过使用泛型,我们可以定义一些通用的接口和抽象类,从而使得实现类可以灵活地指定具体的类型。
这样一来,我们可以提供可复用的接口和抽象类,同时保证类型安全。
4. 类型转换:通过使用泛型,我们可以实现类型的自动转换,从而减少代码的冗余和错误。
通过指定类型参数,我们可以在编译时期检查类型的合法性,从而避免类型转换错误。
java泛型方法Java泛型方法的介绍Java语言是一种使用泛型来提供类型安全性和重用代码的编程语言。
泛型方法是Java语言中的一种特殊语法,它允许在方法中定义一个或多个类型参数。
泛型方法使得我们可以在方法的声明和调用时指定参数的类型,从而实现代码的可重用性和类型安全性。
泛型方法的声明形式为:在方法返回类型之前加上`<T>`,`T`表示泛型类型。
在方法参数列表中可以使用`T`作为参数的类型,也可以使用`T`作为返回值的类型。
使用泛型方法的一个常见的场景是在集合类中的泛型类型参数的推断。
例如,我们可以定义一个泛型方法来计算集合中的最大元素:```javapublic static <T extends Comparable<T>> T getMax(List<T> list) {T max = list.get(0);for (int i = 1; i < list.size(); i++) {if (list.get(i).compareTo(max) > 0) {max = list.get(i);}}return max;}```在这个例子中,我们使用了一个类型参数`T`来表示集合中的元素类型。
`T`被限定为实现了`Comparable`接口的类型,这样我们就可以使用`compareTo()`方法来比较元素的大小。
在方法内部,我们先获取列表中的第一个元素作为初始的最大值,然后遍历列表中的其他元素,逐个比较并更新最大值。
通过泛型方法,我们可以在调用时指定实际类型的集合,而无需在方法中重复编写多个版本的代码。
例如,我们可以这样调用这个方法:```javaList<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);int maxNumber = getMax(numbers);```这样就可以得到集合中的最大整数了。
java方法泛型的定义和使用Java中的泛型是一种参数化类型的概念,它可以在定义类、接口或方法时使用。
泛型的主要目的是为了增加代码的重用性和类型安全性。
在Java中,泛型使用尖括号<>来标识泛型的类型参数,可以在类、接口和方法的定义中使用。
以下是Java方法泛型的定义和使用的示例:1. 定义一个泛型方法:```public <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}}```上述代码中的`<T>`表示定义了一个泛型类型参数T。
`printArray`方法接收一个泛型数组`array`,并逐个打印数组中的元素。
2. 使用泛型方法:```Integer[] intArray = {1, 2, 3, 4, 5};String[] strArray = {"Apple", "Orange", "Banana"};printArray(intArray);printArray(strArray);```上述代码中,`Integer[]`和`String[]`是具体的数组类型,可以传递给`printArray`方法进行打印。
3. 泛型方法的返回类型:```public <T> T getFirstElement(T[] array) {if (array.length > 0) {return array[0];} else {return null;}}```上述代码中的泛型方法`getFirstElement`返回数组的第一个元素,其返回类型是参数化的类型T。
通过使用泛型方法,可以根据实际传入的数组类型确定返回类型。
可以看出,使用泛型方法可以达到代码的灵活性和类型安全的目的。
Java⾯向对象程序设计--泛型编程1. 为何要进⾏泛型编程?泛型变成为不同类型集合提供相同的代码!省去了为不同类型⽽设计不同代码的⿇烦!2. ⼀个简单泛型类的定义:1public class PairTest12 {3public static void main(String[] args)4 {5 String[] arr = {"This","is","the","end","of","the","world"};6 Pair<String> mm = ArrayAlg.minmax(arr);7 System.out.println("min = " + mm.getFirst());8 System.out.println("max = " + mm.getSecond());9 }10 }1112class Pair<T>13 {14public Pair() {first = null; second = null;}15public Pair(T first,T second) {this.first = first; this.second = second;}1617public T getFirst() {return first;}18public T getSecond() {return second;}1920public void setFirst(T newValue) {this.first = newValue;}21public void setSecond(T newValue) {this.second = newValue;}2223private T first;24private T second;25 }2627class ArrayAlg28 {29public static Pair<String> minmax(String[] a)30 {31if(a == null || a.length == 0) return null;32 String min = a[0];33 String max = a[0];34for(int i = 1; i < a.length; i++)35 {36if(pareTo(a[i]) > 0) min = a[i];37if(pareTo(a[i]) < 0) max = a[i];38 }39return new Pair<String>(min,max);40 }41 }上⾯这段代码中定义了⼀个泛型类。
java 泛型函数的定义和使用Java是一种面向对象的编程语言,拥有强大的泛型机制。
泛型函数是一种特殊的函数,可以在定义时使用泛型参数,从而实现对不同类型的数据进行操作。
本文将介绍泛型函数的定义和使用方法。
一、泛型函数的定义在Java中,泛型函数的定义需要在函数名之前使用尖括号<>来声明泛型参数。
泛型参数可以是任意合法的标识符,通常使用大写字母表示。
泛型参数可以在函数的参数列表、返回值类型和函数体中使用,用于表示某种未知的数据类型。
下面是一个简单的泛型函数的定义示例:```public <T> void printArray(T[] array) {for (T item : array) {System.out.println(item);}}```在上述代码中,`<T>`表示泛型参数,`printArray`函数的参数`array`是一个类型为`T`的数组。
在函数体中,可以使用`T`来表示数组中的元素类型。
二、泛型函数的使用泛型函数可以像普通函数一样被调用,但在调用时需要指定实际的类型参数。
可以使用尖括号<>在函数名后面传入实际的类型参数。
下面是一个使用泛型函数的示例:```Integer[] intArray = {1, 2, 3, 4, 5};String[] stringArray = {"Hello", "World"};printArray(intArray); // 调用printArray函数,传入Integer[]类型的参数printArray(stringArray); // 调用printArray函数,传入String[]类型的参数```在上述代码中,分别定义了一个整型数组`intArray`和一个字符串数组`stringArray`。
然后调用了`printArray`函数两次,分别传入了`intArray`和`stringArray`作为参数。
此系列文章译自SUN的泛型编程指南, 看不懂译文的请看原文/j2se/1.5/pdf/generics-tutorial.pdfJava泛型编程指南一、绪言JDK1.5对JAVA语言进行了做了几个扩展,其中一个就是泛型。
本指南旨在介绍泛型。
如果你熟悉其它语言的构造类似的东西,特别是C++的模板(template),你会很快发现它们之间的相同点及重要的不同点;如果你在其他地方没看到过类似的东西,那反而更好,那样你就可以开始全新的学习,用不着去忘掉那些(对JAVA泛型)容易产生误解的东西。
泛型允许你对类型进行抽象。
最常见的例子是容器类型,比如那些在Collection层次下的类型。
下面是那类例子的典型用法:List myIntList = new LinkedList();//1myIntList.add(new Integer(0));//2Integer x = (Integer) myIntList.iterator().next();//3第3行里的强制类型转换有点烦人,程序通常都知道一个特定的链表(list)里存放的是何种类型的数据,但却一定要进行类型转换。
编译器只能保证迭代器返回的是一个对象,要保证对Integer类型变量的赋值是类型安全的话,必须进行类型转换。
类型转换不但会引起程序的混乱,还可能会导致运行时错误,因为程序员可能会犯错误。
如果程序员可以如实地表达他们的意图,即标记一个只能包含特定数据类型的链表,那会怎么样呢?这就是泛型背后的核心思想。
下面是前面代码的泛型写法:List<Integer> myIntList = new LinkedList<Integer>();//1'myIntList.add(new Integer(0));//2'Integer x = myIntList.iterator().next();//3'请注意变量myIntList的类型声明,它指明了这不仅仅是一个任意的List,还是一个Integer类型的List,写作List<Integer>。
我们说List是一个接受类型(在这个例子是Integer)参数的泛华的接口,在创建链表对象的时候,我们也指定了一个类型参数。
另外要注意的是在第3'行的类型转换已经不见了。
现在你可能会想,我们所做的全部都是为了把混乱消除。
我们没有在第3行把类型转换为Integer,而是在第1'行加了Integer类型参数;非也非也,这里面差别很大,编译器现在能够在编译期间检测程序的类型正确性。
当我们把myIntList声明为类型List<Integer>的后,就意味着变量myIntList在何时何地的使用都是正确的,编译器保证了这一点。
相反,类型转换只是告诉我们程序员认为它在程序的某个地方是正确的。
实际的结果是,程序(特别是大型的程序)的可读性和健壮性得到了提高。
免费linux公开课,,现在报名!二、定义简单的泛型下面是java.util包里的List和Iterator接口定义的一个小小的引用:public interface List<E>{void add(E x);Iterator<E> iterator();}public interface Iterator<E>{E next();boolean hasNext();}除了尖括号里的东西,这里所有的都应该很熟悉了。
那是List和Iterator接口的规范类型参数的声明。
类型参数可以用在任何的泛型声明中,就像使用普通的类型一样(虽然有一些很重要的限制;看第7部分)。
在绪言中,我们看到了List泛型声明的调用,比如List<Integer>。
在调用里面(通常称为参数化类型),所有出现规范类型参数(这里是E)的全部都用实际的类型参数(这里是Integer)所代替。
你可以想象成List<Integer>代表所有E都用Integer代替了的List:public interface IntegerList{void add(Integer x)Iterator<Integer> iterator();}这种想法是有所帮助的,但也会造成误解。
它是有所帮助的,是因为参数化类型List<integer>有看起来像这种扩展的方法。
它会造成误解,是因为泛型的声明实际上不会像那样去扩展;在源代码中、二进制文件中、硬盘和内在里,都没有代码的多个拷贝。
如果你是一个C++程序员,你会明白这跟C++的模板(template)很不同。
泛型声明是一次编译,永远使用,它会变成一个单独的class文件,就像一个普通的类或接口声明。
类型参数跟用在方法或构造函数里的普通的参数类似,就像一个方法具有描述它运算用到的值的类型的规范值参一样,泛化声明具有规范类型参数。
当一个方法被调用的时候,实际的参数将会被规范参数所代替而对方法求值。
当一个泛化声明被调用的时候,实际类型参数将会代替规范类型参数。
命名惯例要注意的一个地方。
我们建议你用一些简炼(如果可以的话只用一个字符)但却映眼的名字作为规范类型参数名。
在那些名字中最后避免小写字母,这样可以很容易把规范类型参数和普通的类或接口区分开来。
就像前面的例子一样,很多容器类型使用E。
我们将会在后面的例子里看到其他的惯例。
免费linux公开课,,现在报名!三、泛型和子类化我们来测试一下对泛型的理解,下面的代码是否正确呢?List<String> ls = new ArrayList<String>();//1List<Object> lo = ls;//2第1行肯定是正确的,问题的难点在于第2行;这样就归结为这个问题:一个字符串(String)链表(List)是不是一个对象链表?大部分人的直觉是:“肯定了!”那好,看一下下面这两行:lo.add(new Object());//3String s = ls.get(0);//4:企图把一个对象赋值给字符串!在这里我们把ls和lo搞混淆了。
我们通过别名lo来访问字符串链表ls,插入不确定对象;结果就是ls不再存储字符串,当我们尝试从里面取出数据的时候就会出错。
Java编译器当然不允许这样的事情发生了,所以第2行肯定会编译出错。
一般来说,如果Foo是Bar的子类型(子类或子接口),而G又是某个泛型声明的话,G<Foo>并不是G<Bar>的子类型。
这可能是学习泛型的时候最难的地方,因为它与我们的深层直觉相违背。
直觉出错的问题在于它把集合里的东西假想为不会改变的,我们的本能把这些东西看作是不变的。
举个例子,假设汽车公司为人口调查局提供一份驾驶员的列表,这看上去挺合理。
假设Driver是Person的一个子类,则我们认为List<Driver>是一个List<Person>。
而实际上提交的是一份驾驶员登记表的一个副本。
否则的话,人口调查局将可以驾驶员的人加入到那份列表中去,汽车公司的纪录受到破坏。
为了解决这类问题,我们需要考虑一些更灵活的泛型,到现在为止碰到的规则太受约束了。
四、通配符考虑一下写一个程序来打印一个集合对象(collection)里的所有元素。
在旧版的语言里面,你可以会像下面那样写:void printCollection(Collection c){Iterator i = c.iterator();for (k = 0; k < c.size(); k++){System.out.println(i.next());}}下面尝试着用泛型(和新的for循环语法)来写:void printCollection(Collection<Object> c){for (Object e : c) {System.out.println(e);}}这样的问题是新版本的代码还没旧版本的代码好用。
就像我们刚示范的一样,Collection<Object>并不是所有类型的集合的父类型,所以它只能接受Collection<Object>对象,而旧版的代码却可以把任何类型的集合对象作为参数来调用。
那么,什么才是所有集合类型的父类型呢?这个东西写作Collection<?>(读作“未知集合”),就是元素类型可以为任何类型的集合。
这就是它为什么被称为“通配符类型”的原因。
我们可以这样写:void printCollection(Collection<?> c){for (Object e : c) {System.out.println(e);}}现在,我们就可以以任何类型的集合对象作为参数来调用了。
注意,在printCollection() 方法里面,我们仍然可以从c对象中读取元素并赋予Object类型;因为无论集合里实际包含了什么类型,它肯定是对象,所以是类型安全的。
但对它插入任意的对象的话则是不安全的:Collection<?> c = new ArrayList<String>();c.add(new Object());//编译错误由于我们并不知道c的元素类型是什么,因此我们不能对其插入对象。
add()方法接受类型E,即集合的元素类型的参数。
当实际的类型参数是?的时候,就代表是某未知类型。
任何传递给add方法的参数,其类型必须是该未知类型的子类型。
因为我们并不知道那是什么类型,所以我们传递不了任何参数。
唯一的例外就是null,因为它是任何(对象)类型的成员。
另外,假设有一个List<?>,我们可以调用get()方法并使用其返回结果。
结果类型是一个未知类型,但我们都知道它是一个对象。
因此把get()方法的返回结果赋值给对象类型,或者把它作为一个对象参数传递都是类型安全的。
四、1-有界通配符考虑一个简单的画图程序,它可以画长方形和圆等形状。
为了表示这些形状,你可能会定义这样的一个类层次结构:public abstract class Shape{public abstract void draw(Canvas c);}public class Circle extends Shape{private int x, y, radius;public void draw(Canvas c) { ... }public class Rectangle extends Shape {private int x, y, width, height;public void draw(Canvas c) { ... }}这些类可以在canvas上描画:public class Canvas {public void draw(Shape s) {s.draw(this);}}任何的描画通常都包括有几种形状,假设它们用一个链表来表示,那么如果在Canvas里面有一个方法来画出所有的形状的话,那将会很方便:public void drawAll(List<Shape> shapes) {for (Shape s: shapes) {s.draw(this);}}但是现在,类型的规则说drawAll()方法只能对确切的Shape类型链表调用,比如,它不能对List<Circle>类型调用该方法。