Java Generics教程
在本教程中,我们将看到有很多例子的Java Generics教程。
java中的泛型
'泛型'是如今非常常见的术语。
语法,它是一个"通用"这个词的集体,这意味着一定的特征,它不是非常特异性的,并且可以用于表示某种范围或者一类。
正如我们所知,作为面向对象的编程语言,受到真实例子中汲取的概念的很大影响,让我们试图为Java带来泛型的概念。
让我们以仿制药的想法,从一个简单的现实生活中的例子。
我相信我们所有人都有不幸的是打开一盒饼干并发现一堆缝纫用品,至少一次。
因此,让我们说cookie框是一种通用实例,可以采用不同的参数(饼干或者缝纫耗材),并根据从用户接收的参数的类型不同地行为(作为曲柄盒或者缝制电源)。
泛型主要用于编译类型安全性,避免ClassCasteException的机会。
假设我们有以下代码不使用泛型。
我们需要创建一个只包含整数类型值的列表。
List integerList=new ArrayList(); integerList.add(new Integer(20)); Integer i=(Integer) integerList.get(0); integerList.add(new String("Twenty")); //Valid Integer i2=(Integer) integerList.get(1);
在第4行,当我们从列表中检索值时,我们需要再次投入整数。
这是一种恼人的程序员,当你检索列表中的值施放每次。
在第5行,我们能够将字符串添加到Integerlist !!这很糟糕,我们不想那样。
当我们在第6行检索值整数员时,我们将为我们提供ClassCastException。
让我们在上面代码中实现泛型。
List<Integer> integerList=new ArrayList<Integer>(); integerList.add(new Integer(20)); Integer i=integerList.get(0); integerList.add(new String("Twenty")); //compile time error Integer i2=(Integer) integerList.get(1);
正如我们所看到的,我们不需要在No.4号线演出。
我们无法将字符串添加到Integerlist,这很棒。
这是要求。
这是一个结果,我们不会再获得ClassCastException。
为什么仿古?
让我们说,我们必须根据用户提供的输入对数字,字符和字符串进行排序。
在这种情况下,"原始"或者"非通用"方法是用相同的排序技术(假设泡沫分类)写三个函数,或者将所有输入作为字符串键入并作为字符串进行排序数组,它再次成为效率低下的过程,因为我们必须在比较之前键入每个输入到串。
如果我们可以为所有这些输入使用一个排序函数而无需每次键入演员,它不会更有效和耗时更少?
那么这正是Java中的泛型设计 - 让程序员通过写入更少的代码来实现更多的自由。
使用泛型对非泛型实践的优点
- 强类型安全 - Java编译器在编译时对通用类型代码执行严格检查,并评估是否允许到特定通用代码的类型分配。这可以防止大量可能的运行时错误和classcastExceptions,它可能会停止应用程序的处理。由于编译时间检查更易于跟踪和解决,使用泛型减少程序员的工作。
- 不需要类型铸造 - 不使用泛型,使用不同类型的数据处理将需要多次铸造。泛型通过为要使用的所有类型提供公共字段来解决此问题。
- 创建有效的广义算法 - 如上一节所述,使用泛型允许程序员写入更少的代码行并实现更多。使用泛型编写不同类型的可重复使用代码是从非通用或者原始算法向前的巨大飞跃。
Java如何工作如何工作?
Java中的泛型工作在两个主要原理 - "类型安全"和"型擦除"中。
在上一节中,我们已经讨论过,通过在编译时间执行严格类型检查而不是允许代码编译并允许运行时间错误或者异常,或者允许运行时间错误或者例外情况来确保型安全性。
另一方面,键入擦除,以类似于使用橡皮擦的方式工作。
在编译期间,包含泛型的源代码从生成的字节代码中删除并替换为声明的类型本身,类似于Java 1.5之前的遗留代码,其中泛型被引入概念。
这允许向后兼容传统非通用代码。
Java的泛型及其分类
Java中的泛型可以在两个标题和方法下分类。
我们将在以下部分进一步详细讨论各种类型。
类仿制
类构成Java中所有结构的基础,并将所有属性和行为封装到单个单元中。
通常,我们将类视为类型本身,我们可以用作对象变量。
但是,如果需要我们创建与不同类型的数据不同的行为不同的类别,那么
让我们说,与整数的字符串不同的行为。
这是为了此目的,类可以被声明为通用,并以不同的类型参数表现不同。
我们如何声明一般程序?
- 可以像Java中的任何其他类声明一样声明通用类,在类名声明后添加通用类型参数。与泛型方法中的类型参数类似,这些也可以包含由逗号分隔的多种类型参数。
- 泛型类通常被称为"参数化类",因为这些类在对象创建期间接受类型参数。
通用类的声明形成如下:类
示例声明是:
public class GenClassTest<T>{}
让我们在一个例子中使用上述声明。
public class GenClassTest<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } public static void main(String[] args) { GenClassTest<Integer> gcInt = new GenClassTest<Integer>(); GenClassTest<String> gcStr = new GenClassTest<String>(); System.out.println("Invoking the class with Integer"); gcInt.setData(5); System.out.println("Integer Data: "+gcInt.getData()); System.out.println("Invoking the class with String"); gcStr.setData("Five"); System.out.println("Integer Data: "+gcStr.getData()); } }
执行上述代码时,生成以下输出:
Invoking the class with Integer Integer Data: 5 Invoking the class with String Integer Data: Five
方法/函数作为泛型
正如我们在上一节中讨论的那样,可以写入单个通用方法,可以使用不同类型的参数调用。
根据传递给该方法的参数类型,编译器将每个方法调用作为单独的调用。
在Java中编写泛型方法不是似乎的不可逾越的任务。
相反,它很简单。
在声明和使用泛型时,必须记住某些关键点。
我们如何声明一般方法?
可以像Java中的任何其他方法声明一样声明通用方法,请记住以下规则:
- 它们必须具有由一对角括号(<和>)标记的方法签名的类型参数部分,并且在方法返回类型声明之前将此部分放置
- 声明的每个类型参数部分都可以包含多个类型的变量,并且它们由逗号分隔。它是定义通用类型名称的标识符。
- 类型参数也可以用作方法的返回类型,并倾向于根据传递给该方法的参数类型等占位符
- Generic方法类型参数仅处理引用类型(对象),无法支持原始类型数据(INT,LONG,FLOAT,DOUID等)
因此,用于通用方法的示例方法原型是:
public static <E> void printData(E inputList)
让我们将上述方法称为一个完整的例子。
package org.igi.theitroad; public class GenMethodTest{ public static <E> void printData(E[] inputList){ for(E current:inputList){ System.out.print(current+" "); } System.out.println("\n"); } public static void main(String[] args) { Integer aInt[]={1,2,3}; String aStr[]={"Hello","World"}; Character aChar[]={'G','O','O','D'}; System.out.println("Integer Data:"); printData(aInt); System.out.println("String Data:"); printData(aStr); System.out.println("Character Data:"); printData(aChar); } }
在执行上述代码时,获得以下输出:
Integer Data: 1 2 3String Data: Hello WorldCharacter Data: G O O D
泛型通配符
我们可以使用 ?
用泛型。
它代表未知类型。
让我们先了解为什么需要它。
List<Integer> integerList=new ArrayList<Integer>(); List<Number> numberList=integerList;
我们认为上面的代码是否将编译。
说"OFCOURSE"非常直观,但我们将在第2行的编译时间错误。
让我们了解原因。
如果允许上述代码,我们将能够添加双倍,浮动到NumberList,这意味着我们能够添加双倍并漂浮到整数列表,这将打破泛型的概念。
public static void printList(List<Object; list) { System.out.println(list.toString()); }
并调用函数具有以下代码。
List<Integer> integerList=new ArrayList<Integer>(); integerList.add(4); integerList.add(6); printList(integerList);
我们将在上面的代码中的第5行中获取编译错误。
我们无法将列表传递给将列表<Object>作为参数的函数。
我们可以使用通配符(?
)来解决上述问题。
当我们使用通配符时,我们可以将任何类型的列表传递给函数。
public static void printList(List<?> list){ System.out.println(list.toString()); }
我们可以将所有类型的列表传递给PrintList函数。
通配符的型号
未绑定的通配符
这被称为未知类型的通配符.Above示例仅为未绑定通配符的示例。
上有限
我们想编写一个可以占用列表<整数>的方法,列表<double>和list <long>但不是列表<string>。
下图将更清晰
我们可以如下写入此方法。
public static void printList(List<? extends Number> list){ System.out.println(list.toString()); }
这意味着我们可以通过所有类型的列表,这些列表是数字类的子类,如整数,双倍和长,但我们无法通过任何其他类型的列表。
语法:
CollectionType<? extends A>
下界通配符
我们想编写一个可以拿出列表<整数>的方法,列表<number>但不是列表<double> .below图会更清晰
我们可以如下写入此方法。
public static void printList(List<? super Number> list) { System.out.println(list.toString()); }
这意味着我们可以通过所有类型的列表,这些列表是超小时类的超级类,但对象,但我们无法通过任何其他类型(如双)列表。
语法:
CollectionType<? super A>
使用'有界类型参数'
既然我们已经讨论了泛型和类型参数,我们已理解,使用泛型类型参数允许程序员将任何引用类型设置为类型参数。
但是,如果我们想限制这种自由,只允许某些类型?
这是通过使用有界类型的参数处理.Since Java由对象管理,每个引用类型都有它所属的父对象。
要绑定参数类型,我们声明了类型参数后跟延伸关键字(或者在接口的情况下的实施例),后跟上限类或者interface.let Us请参阅使用界限类型的示例,其中包含以下代码至少两个数字。
public class FindSmaller { public static <T extends Comparable<T>> T findSmaller(T a,T b){ //accepts only comparable objects if(a.compareTo(b)<0) return a; else return b; } public static void main(String[] args) { //TODO Auto-generated method stub System.out.println("The Smaller between 5 and 6 is: "+findSmaller(5,6)); System.out.println("The Smaller between Hello and World is: "+findSmaller("Hello","World")); } }
上述代码的输出是:
The Smaller between 5 and 6 is: 5The Smaller between Hello and World is: Hello
泛型的局限性
在整个本文中,我们已经看到泛型是一种光荣的救世主,被引入拯救我们从编写更长的源代码。
然而,就像每一件好事一样,甚至泛型都有一定的限制。
- 泛型类型成员或者变量不能被声明为静态。
- 通常无法实例化通用类型 - 例如: - 我们无法实例化通用类型。
public class Example<E>{ Example(){ new E(); //NOT ALLOWED } }
- 泛型仅使用对象和包装程序的原始类型,但不是原始类型本身
- Generic类型不能用于创建自定义异常。
- 泛型不允许子类型
List<Dog> = new ArrayList<Animal>(); //NOT ALLOWED
- 无法实例化/创建通用阵列,但可以声明。
E[] arr; //ALLOWEDE[] arr = new E[10]; //NOT ALLOWED