一句话理解
泛型是一种检查机制,使得在编译时检测出错误,而不是运行时。
优点
加强安全性。
知识要点(详细)
以下例子来源:Java语言程序设计(进阶篇)
定义的泛型类会在后续的例子中持续使用
1.定义泛型类&接口
import java.util.ArrayList;
//定义泛型类
public class GenericStack <E>{
ArrayList<E> list = new ArrayList<>();
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();
}
@Override
public String toString() {
return "stack:" + list.toString() ;
}
}
//应用泛型类添加元素,再输出
public class GenericDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
GenericStack<String> stack1 = new GenericStack<>();
stack1.push("London");
stack1.push("Paris");
stack1.push("Berlin");
GenericStack<Integer> stack2 = new GenericStack<>();
stack2.push(1);
stack2.push(2);
stack2.push(3);
System.out.println(stack1.pop());
System.out.println(stack1.pop());
System.out.println(stack1.pop());
System.out.println();
System.out.println(stack2.pop());
System.out.println(stack2.pop());
System.out.println(stack2.pop());
System.out.println();
}
}
注意这是一个栈类
2.定义泛型方法
package test;
public class GenNewDemo {
//定义泛型方法
public static <E> void print (E[] list){
for(int i = 0;i<list.length;i++){
System.out.print(list[i]+ " ");
System.out.println();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Integer[] integeres = {1,2,3,4,5};
String[] strings = {"London","Paris","New York"};
print(integeres);
System.out.println();
//以下三种输出方式都是正确的
print(strings);
System.out.println();
GenNewDemo.print(strings);
System.out.println();
GenNewDemo.<String>print(strings);
}
}
注意:
是可以为静态方法定义泛型类型。
3.受限泛型&非受限泛型
受限泛型:泛型指定为另外一种类型的子类型。eg:<E entends GenericOperation> (GenericOperation是一个自定义类)
非受限泛型:假设有一个<E>,它就等同于<E entends Object>
4.原始类型及向后兼容
原始类型:没有指定具体类型的泛型类和泛型接口被称为原始类型。
eg:
GenericStack stack = new GenericStack();
//大体等价于
GenericStack<Object> stack = new GenericStack<Object>();
像这样不带类型参数的泛型类就是原始类型。
注意:原始类型是不安全的。
5.通配泛型(重点)
通配泛型分类:
(1)非受限通配 ?
和? exends Object是一样的
(2)受限通配 <? extends T >
表示T或T的一个子类型
(3)下限通配 <? super T >
表示T或T的一个父类型
?应用举例:
package test;
public class WildCardNeedDemo {
//找到stack中的最大数字
public static double max(GenericStack<? extends Number> stack){
double max = stack.pop().doubleValue();
while(!stack.isEmpty()){
double value = stack.pop().doubleValue();
if(value>max){
max = value;
}
}
return max;
}
public static void print (GenericStack<Object> stack){
while(!stack.isEmpty()){
System.out.print(stack.pop() + " ");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
GenericStack<Integer> intStack = new GenericStack<>();
intStack.push(1);
intStack.push(2);
intStack.push(-2);
print(intStack);
}
}
错误分析:虽然Integer是Object的子类型,但是GenericStack<Integer>不是GenericStack<Object>的子类型
修改代码为
运行结果
package test;
public class WildCardNeedDemo {
//找到stack中的最大数字
public static double max(GenericStack<Number> stack){
double max = stack.pop().doubleValue();
while(!stack.isEmpty()){
double value = stack.pop().doubleValue();
if(value>max){
max = value;
}
}
return max;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
GenericStack<Integer> intStack = new GenericStack<>();
intStack.push(1);
intStack.push(2);
intStack.push(-2);
System.out.println("The max number is : " + max(intStack));
}
}
错误分析:虽然Integer是Number的子类型,但是GenericStack<Integer>不是GenericStack<Number>的子类型,所以要用通配泛型去解决问题。
修改代码为
运行结果
<? super T >应用举例:
package test;
public class SuperWildCardDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
GenericStack<String> stack1 = new GenericStack<>();
GenericStack<Object> stack2 = new GenericStack<>();
stack2.push("Java");
stack2.push(2);
stack1.push("Sun");
add(stack1,stack2);
print(stack2);
}
private static <T> void add(GenericStack<T> stack1, GenericStack<? super T> stack2) {
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
public static void print (GenericStack<?> stack){
while(!stack.isEmpty()){
System.out.println(stack.pop() + " ");
}
}
}
运行结果
6.消除泛型
泛型在编译时,一旦编译器确认泛型类型是安全使用的,就会将它转换为原始类型。
例如:
确认安全后,编译器转换为Object类型代替泛型类型
7.泛型的限制
(1)不能使用new E();
eg:E object = new E();
(2)不能使用new E[ ]
eg:E[ ] elements = new E[ ];
(3)在静态上下文中不允许类的参数是泛型类型
由于泛型类的所有实例都有相同的运行时类,所以泛型类的静态变量和方法是被它的所有实例共享,因此,在静态方法、数据域或者初始化语句中,为类引用泛型类型参数是非法的,非法代码举例如下:
public class Test<E>{
public static void m(E o1){
//Illegal
}
public static E o1;//Illegal
static {
E o2;//Illegal
}
}
(4)异常类不能是泛型的
必须添加try/catch
泛型类不能扩展java.lang.Throwable
下面类的声明是非法的:
public class MyException<T> extends Exception{
}
如果允许,要为MyException<T> 添加一个catch语句
try{
}catch( MyException<T> ex){
}
JVM必须检查这个try句子抛出的异常,来确定是否与catch子句中指定的类型匹配,但是这是不可能的,因为运行时类型信息是不可得的。
总结
1.泛型具有参数化类型的能力,可以定义使用泛型类型的类或方法,编译器会用具体的类型来替换泛型类型。
2.泛型主要优势是能够在编译时检测错误,而不是运行时。
3.泛型类或方法允许指定这个类或方法可以带有的对象类型,如果试图使用带有不兼容对象的类或方法,编译器会检测出这个错误。
4.定义在类、接口或者静态方法中的泛型称为形式泛型类型,随后可以用一个实际具体类型来替换它。替换泛型类型的过程称为泛型实例化。
5.使用原始类型是为了向后兼容java较早的版本,因为JDK1.5之前是没有泛型的。
6.使用类型消除的方法来实现泛型。编译器使用泛型类型信息来编译代码,但是随后消除它。因此,泛型信息在运行时是不可用的。这个方法能够使泛型代码向后兼容使用原始类型的遗留代码。
7.不能使用泛型类型参数创建实例。
8.不能使用泛型类型参数创建数组。
9.不能在静态环境中使用类的泛型参数。
10.在异常类中不能使用泛型类型参数。
如有错误,欢迎指正。