泛型(generic):是指参数化类型的能力。可以定义泛型类和泛型方法,随后编译器会用具体的类型来替换它。
使用泛型的主要优点是,能够在编译时而不是在运行时检测出错误。比如:
public interface Comparable { public interface Comparable<T> {
public int compareTo(Object o); public int compareTo(T o);
} }
a)在jdk1.5之前 b)在jdk1.5
这里的<T>表示形式泛型类型,随后可以用一个具体的类型代替。替换泛型类型称为泛型实例化。
Comparable c = new Date(); Comparable<Date> c = new Date();
System.out.println(c.compareTo("color")); System.out.println(c.compareTo("color"));
c)在jdk1.5之前 d)在jdk1.5
c中的代码可以通过编译,但是在运行时会报一个类型转换异常,因为Date不能和一个String做比较。
d中的代码在编译时就会报错,因为传递给compateTo方法的必须是一个Date类型。
1、定义泛型类和接口
public class GenericStack<E> { private ArrayList<E> list = new ArrayList<E>(); public int getSize() { return list.size(); } public E peek() { return list.get(getSize() - 1); } public void push(E o) { list.add(o); } public E pop() { E o = list.get(getSize() - 1); list.remove(getSize() - 1); return o; } public boolean isEmpty() { return list.isEmpty(); } }
2、泛型方法
public class GenericMethod { public static <E> void print(E[] array) { for(E o : array) { System.out.println(o); } } public static void main(String[] args) { Integer[] integer = {1, 2, 3, 4}; String[] string = {"a", "b", "c", "d"}; print(integer); print(string); } }
3、泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
实例:
public class LinkedStack<T> { private class Node { T item; Node next; Node() { item = null; next = null; } Node(T item, Node next) { this.item = item; this.next = next; } boolean end() { return item == null && next == null; } } private Node top = new Node(); // End sentinel public void push(T item) { top = new Node(item, top); } public T pop() { T result = top.item; if (!top.end()) top = top.next; return result; } public static void main(String[] args) { LinkedStack<Number> lss1 = new LinkedStack<Number>(); LinkedStack<Integer> lss2 = new LinkedStack<Integer>(); System.out.println(lss1.getClass()); System.out.println(lss2.getClass()); } }
输出结果:
class generic.LinkedStack
class generic.LinkedStack
true
由此,我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本实例中为LinkedStack),当然,在逻辑上我们可以理解成多个不同的泛型类型。
究其原因,在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
4、类型通配符
接着上面的示例探讨,从上面的示例我们知道LinkedStack<Number>和LinkedStack<Integer>都是LinkedStack类型,那么在逻辑上LinkedStack<Number>和LinkedStack<Integer>是否可以看成具有父子关系的类呢?
public static void main(String[] args) {
LinkedStack<Number> lss1 = new LinkedStack<Number>();
LinkedStack<Integer> lss2 = new LinkedStack<Integer>();
getDate(lss1);
getDate(lss2);
}
public static void getDate(LinkedStack<Number> data) {
System.out.println(data.getClass());
}
代码中的getDate(lss2);这一行编译错误。The method getDate(LinkedStack<Number>) in the type LinkedStack<T> is not applicable for the arguments (LinkedStack<Integer>)
因此,在逻辑上LinkedStack<Number>不能视为LinkedStack<Integer>的父类。
那么怎么解决这种问题呢?在这里java提供了通配符类型,类型通配符一般用?表示。
public static void main(String[] args) {
LinkedStack<Number> lss1 = new LinkedStack<Number>();
LinkedStack<Integer> lss2 = new LinkedStack<Integer>();
getDate(lss1);
getDate(lss2);
}
public static void getDate(LinkedStack<?> data) {
System.out.println(data.getClass());
}
当用了通配符之后,错误就没有了,代码可以正确运行。
有时候,我们还可能听到类型通配符上限和类型通配符下限。具体有是怎么样的呢?
在上面的例子中,如果需要定义一个功能类似于getData()的方法,但对类型实参又有进一步的限制:只能是Number类及其子类。此时,需要用到类型通配符上限。
public static void main(String[] args) {
LinkedStack<Number> lss1 = new LinkedStack<Number>();
LinkedStack<Integer> lss2 = new LinkedStack<Integer>();
LinkedStack<String> lss3 = new LinkedStack<String>();
getDate(lss1);
getDate(lss2);
getDate(lss3);
}
public static void getDate(LinkedStack<? extends Number> data) {
System.out.println(data.getClass());
}
此时,显然,在代码getDate(lss3);处调用将出现错误提示。
类型通配符上限通过形如LinkedStack<? extends Number>形式定义,相对应的,类型通配符下限为Box<? super Number>形式,其含义与类型通配符上限正好相反,在此不作过多阐述了。
5、消除泛型和对泛型的限制
泛型是通过一种称为类型消除的方法来实现的。编译器使用泛型信息来编译代码,但是随后会消除它。因此,泛型信息在运行时是不可见的。
泛型存在于编译时。一旦编译器确定泛型类型时安全使用的,就会将它转换为原始类型。
示例:
ArrayList<String> list = new ArrayList<String>(); ArrayList list = new ArrayList();
last.add("test"); 转换为: last.add("test");
String str = list.get(0); String str = (String)list.get(0);
public static void getDate(T[] data) { public static void getDate(Object[] data) {
System.out.println(data.length); 转换为: System.out.println(data.length);
} }
对泛型的限制
1.不能使用new E()
E e = new E();这是错误的,因为运行时,E不可用
2.不能使用new E[]
E[] e = new E[2];这是错误的
规避方法
E[] e = (E())new Object[2];
也不能使用泛型类型创建数组
ArrayList<String> list = new ArrayList<String>[10];
规避方法
ArrayList<String> list = (ArrayList<String>)new ArrayList[10];
3.在静态环境下不允许类的参数是泛型类型
泛型类的静态变量,静态方法和初始化语句中引用泛型类型参数是非法的。
public void Test<E> {
public static void m(E 0) { //非法
}
public static E o; //非法
static {
E o;//非法
}
}
4、异常不能是泛型的
泛型类不能继承Throwable。
相关推荐
Java泛型机制详解 Java泛型是Java语言中的一种机制,用于在编译期检查类型安全。Java泛型的出现解决了Java早期版本中类型安全检查的缺陷。Java泛型的好处是可以在编译期检查类型安全,避免了运行时的...
Java泛型是Java SE 5.0引入的一个重要特性,极大地提高了代码的类型安全性和重用性。在本文中,我们将深入探讨Java泛型的进阶概念,包括通配符、边界、类型擦除以及在实际开发中的应用。 1. 通配符 通配符在Java...
以下是对Java泛型的详细解释: 1. **泛型类型**:泛型类型允许我们在定义类、接口或集合时引入类型参数。比如`List<String>`就是一个泛型类型,其中String是类型参数,代表了列表中的元素类型。这样,当我们将字符...
思维导图之Java泛型详解
"Java 泛型总结(一):基本用法与类型擦除" Java 泛型是 Java 语言中的一种强大功能,它可以使代码更加简洁、安全。下面是对 Java 泛型的基本用法和类型擦除机制的介绍。 泛型的基本用法 ------------- 泛型是...
总结,Java泛型技术的发展极大地提升了Java编程的效率和安全性。它通过编译时检查和类型安全保证,帮助开发者编写出更加健壮和易于维护的代码。理解并熟练掌握Java泛型,是每一个Java开发者必备的技能。
下面我们将深入探讨Java泛型方法的概念、语法以及使用示例。 **一、泛型方法概念** 泛型方法是一种具有类型参数的方法,这些类型参数可以在方法声明时指定,并在方法体内部使用。与类的泛型类似,它们提供了编译时...
Java 泛型总结(二):泛型与数组 Java 中泛型数组的关系确实有些复杂,不允许直接创建泛型数组,本文将分析其中的原因,并总结一些创建泛型数组的方式。 首先,让我们看看数组和泛型的关系。数组相比于 Java ...
Java泛型是Java编程语言中的一个关键特性,它在2004年随着JDK 5.0的发布被引入,极大地增强了代码的类型安全性和重用性。泛型允许我们在编写类、接口和方法时指定参数化类型,使得代码在编译时期就能捕获类型错误,...
Java 泛型总结(三):通配符的使用 通配符是 Java 泛型中一个重要的概念,它允许我们在泛型中实现向上转型,解决了泛型不支持协变的问题。本文将详细介绍通配符的使用和原理。 在 Java 中,数组是协变的,这意味...
### Java泛型的使用详细讲解 #### 一、引言 在Java开发中,泛型是一种重要的语言特性,它能够帮助开发者在不增加代码量的情况下处理多种数据类型,同时还能保持代码的清晰度和可读性。本文将详细介绍Java泛型的...
通过理解和掌握Java泛型,开发者可以在编写代码时得到更多编译器的帮助,增强程序的类型安全性,同时降低运行时异常的风险,提高代码质量。在实际开发中,合理使用泛型可以显著提升代码的可读性、可维护性和效率。
总结来说,Java泛型的类型擦除虽然在运行时消除了类型信息,但通过编译时的类型检查、桥接方法、通配符等补偿机制,仍然实现了强大的类型安全和便利性。开发者应理解这些补偿机制,以便更好地利用Java泛型进行类型...
标题与描述均提到了“全面总结Java泛型--实例”,这表明文章旨在深入解析Java泛型的概念,并通过具体示例来展示其应用。Java泛型是Java编程语言的一个强大特性,它允许在编译时检查类型安全,并且所有的强制转换都是...
Java 泛型是 Java 编程语言中一个强大的特性,它允许在类、接口和方法中使用类型参数,从而增加了代码的复用性和安全性。在本文中,我们将深入探讨 Java 泛型的各种方面。 首先,普通泛型允许我们在定义类时引入一...
总结来说,Java泛型与容器详细笔记提供了关于如何使用Java中的泛型和容器类的深入理解,涵盖了Java集合框架的核心组件,泛型的类型安全机制,以及一些提高开发效率的第三方库。文档通过实例代码展示了如何在实际项目...