泛型
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
泛型方法
它在调用时会接受不同的类型的参数,根据传入的类型,编译器适当地处理每一个方法调用
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 )。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。
- 泛型方法始终以自己定义的类型参数为准
实例:
package com.Factory;
class T<T> {
private T value;
public void getValue(T value)
{
System.out.println(value.getClass().getName());
}
public <T> void setValue(T value)
{
System.out.println(value.getClass().getName());
}
}
public class Test24
{
public static void main(String[] args)
{
T<String> t = new T<String>();
t.getValue("hello");
t.<Integer>setValue(123);
}
}
从上面的代码可以看出,虽然泛型类和泛型方法都用的是泛型T,但是泛型中的类型参数和泛型方法当中地类型参数没有联系,getValue方法输入的是String,而setValue方法存入的是int。
泛型擦除
泛型擦除是指Java中的泛型只在编译期有效,在运行期间会被删除。也就是说所有泛型参数在编译后都会被清除掉。
如在代码中定义List和List等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的。
实例:
public class Test26 {
public static void main(String[]args)
{
List<String> list1 = new ArrayList<String>();
List<Integer> list2 = new ArrayList<Integer>();
System.out.println(list1.getClass()==list2.getClass());
}
}
说明泛型类型String和Integer都被擦除掉了,只剩下原始类型。
原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。
println
class println<T> {
T name;
public void setname(T name) {
this.name=name;
}
public T getname(){
return name;
}
}
println原始类型:
class println {
Object name;
public void setname(Object name) {
this.name=name;
}
public Object getname(){
return name;
}
}
因为在println中,T 是一个无限定的类型变量,所以用Object替换,其结果就是一个普通的类,如同泛型加入Java语言之前的已经实现的样子。在程序中可以包含不同类型的println,如println或println,但是擦除类型后他们的就成为原始的println类型了,原始类型都是Object。
通配符
2、固定上边界的通配符(Upper Bounded Wildcards)
使用固定上边界的通配符的泛型,就能够接受指定类及其子类类型的数据。要声明使用该类通配符,采用 <? extends E> 的形式,这里的 E 就是该泛型的上边界。
注意:这里虽然用的是 extends 关键字,却不仅限于继承了父类 E 的子类,也可以代指实现了接口 E 的类。
List<? extends 类型1> list1 = new ArrayList<类型2>();
类型1指定一个数据类型,那么类型2就只能是类型1或者是类型1的子类:
List<? extends Number> list1 = new ArrayList<Integer>();//这是正确的
List<? extends Number> list2 = new ArrayList<String>();//这是错误的
这是可以的:
List<? extends Object> list1 = new ArrayList<Integer>();
List<? extends Object> list2 = new ArrayList<String>();
3、固定下边界的通配符(Lower Bounded Wildcards)
使用固定下边界的通配符的泛型,就能够接受指定类及其父类类型的数据。要声明使用该类通配符,采用 <? super E> 的形式,这里的 E 就是该泛型的下边界。
注意:你可以为一个泛型指定上边界或下边界,但是不能同时指定上下边界。
List<? super 类型1> list1 = new ArrayList<类型2>();
类型1指定一个数据类型,那么类型2就只能是类型1或者是类型1的父类
List<? super String> list1 = new ArrayList<Number>();//错
List<? super Integer> list2 = new ArrayList<Number>();//对
注:限定通配符总是包括自身。