JAVA泛型
Java 泛型(generic) 是在JDK 1.5版本引用的一种新的特性,泛型提供编译时安全检查机制,该机制允许程序员在编译时检查非安全的类型。
一、泛型本质
泛型本质是数据化类型,即先给类型指定一个参数,然后使用时再指定参数具体的值,那么这个类型可以在使用时候决定,这种参数类型可以用在类、接口、方法中,分别被称为泛型类、泛型接口、泛型方法。
List<String> list = new ArrayList<>();
二 、为什么使用泛型
泛型好处在编译时候检测类型安全,并且泛型强制转换都是自动或者隐式的,提高代码重用性。
泛型作用:1、确保类型安全性,2、消除类型强制转换,3、提高代码性能,4、提高代码重用性
1、确保类型安全性
在没有泛型之前,从集合中读取数据都需要类型转换,如果一不小心将插入错误的类型对象,在运行时转换处理就会出错。
没有泛型前
ArrayList list = new ArrayList();
list.add("ChenBinBin");
list.add(9527); //编译不报错,
使用泛型后
ArrayList<String> list = new ArrayList<>();
list.add("ChenBinBin");
list.add(9527); //编译时报错
使用泛型后,定义好的集合list在编译时候添加整数类型add(9527)会报错,
2、消除类型强制转换
泛型优点之一:消除代码中许多类型的强制转换,提高代码的可读性,并且减少出错的机会。
泛型之前
ArrayList nameList = new ArrayList();
nameList.add("ChenBinBin");
String name = (String) nameList.get(0);//获取集合对象时候,需要强制转换类型
System.out.println(name);
泛型之后
ArrayList<String> nameList = new ArrayList<>();
nameList.add("ChenBinBin");
String name = nameList.get(0); //不需要强制转换
System.out.println(name);
3、提高代码性能
避免了不必要的装箱(Boxing)和拆箱(UnBoxing)操作
在泛型之前,简单类型object对象进行传递时会引起装箱(Boxing)和拆箱(UnBoxing)操作,这个有很大的开销.
在泛型之后,避免了不必要的装箱(Boxing)和拆箱(UnBoxing)操作.运行效率很高,尤其是对集合对象频繁操作,提升效果更加明显.
泛型变量固定了类型,使用的时候已经知道值类型是基本类型还是引用类型.从而避免了不必要的装箱和拆箱操作.
泛型之前
package com.chen.test.Demo;
public class NonGeneric {
Object a = 1; //由于类型是Object,所以需要装箱操作
int b = (int)a;//a对象是Object类型,传递给int类型,需要强制转换,拆箱操作
}
泛型之后
package com.chen.test.Demo;
public class JavaGeneric<T> {
public T value;
public JavaGeneric(T value) {
this.value = value;
}
public static void main(String[] args) {
JavaGeneric<Integer> javaGeneric = new JavaGeneric<Integer>(1);
}
}
4、提高代码重用性
三、如何使用泛型类型
泛型可以使用在类、接口、方法,分别称为泛型类、泛型接口、泛型方法。
1、泛型类
泛型类定义格式:
public class 泛型类名 <泛型类型参数1,...>{}
注意: 泛型类型必须是引用类型,而非基本类型
,当然泛型类型参数一般都是大写字母参数形式:
T :任意类型type
K :kay-value形式key
V :key-value形式value
E :集合中元素类型element
泛型类:
package com.chen.test.Demo.Generic;
public class GenericClass<T> {
public T value;
public GenericClass(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
//main Method
public static void main(String[] args) {
GenericClass<String> name = new GenericClass<>("ChenBinBin");
System.out.println(name.getValue());
name.setValue("YangJingJing");
System.out.println(name.getValue());
GenericClass<Integer> numStr = new GenericClass<>(9527);
System.out.println(numStr.getValue());
}
}
泛型类测试结果:
2、泛型接口
泛型接口定义格式:
public interface 接口名称<泛型类型1,...> {}
泛型接口
package com.chen.test.Demo.Generic;
public interface GenericInterface<T> {
void showValue(T value);
}
写Integer和String两个泛型实现类
package com.chen.test.Demo.Generic;
public class IntegerShowValueImpl implements GenericInterface<Integer>{
@Override
public void showValue(Integer value) {
System.out.println(value);
}
}
package com.chen.test.Demo.Generic;
public class StringShowValueImpl implements GenericInterface<String>{
@Override
public void showValue(String value) {
System.out.println(value);
}
}
单元测试类
package com.chen.test;
import com.chen.test.Demo.Generic.IntegerShowValueImpl;
import com.chen.test.Demo.Generic.StringShowValueImpl;
public class GenericTest {
public static void main(String[] args) {
new StringShowValueImpl().showValue("ChenBinBin");
new IntegerShowValueImpl().showValue(9527);
}
}
测试结果:
值得注意: 使用泛型,前后类型需要一致性
3、泛型方法
泛型方法:是在调用方法的时候指明泛型的具体类型。
泛型方法定义格式
修饰符 <泛型参数变量> 返回类型 方法名(泛型参数类型){}
package com.chen.test.Demo.Generic;
public class GenericClass<T> {
public GenericClass(T t) {
}
public GenericClass() {
}
public <T> T genericMethod(T value){
System.out.println(value.getClass());
System.out.println(value);
return value;
}
public static void main(String[] args) {
GenericClass<StringBuffer> g1 = new GenericClass("Hello ChenBinBin");//该类型可以定义多种形式
String name = g1.genericMethod("ChenBinBin "); //传入String类型,返回String类型
Integer num = g1.genericMethod(9527); //传入Integer类型,返回Integer类型
GenericClass<Integer> g2 = new GenericClass();//该类型可以定义多种形式
String name2 = g2.genericMethod("YangJingJing");
}
}
值得注意: 泛型方法随着传入参数类型不同,返回类型也不同。泛型方法能是方法独立于类而参数变化
四、泛型通配符
Java 泛型通配符是用于解决泛型之间引用传递问题的特殊处理语法,主要有三种:无边界通配符、有上边界通配符、有下边界通配符
注意: 泛型仅仅存在编译时,在转换为字节码的时候会将泛型擦除
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}
1、无边界通配符
格式形式:<?>
List<?> 表示传递的类型不知道是什么类型
展示代码
List<?> list = new ArrayList<Integer>(); //泛型通配符,,定义对象不能添加元素
//list.add(1);编译时报错
2、有上边界通配符
格式表达:
<? extends T> 表示传递类型必须是T以及T子类型
展示代码
package com.chen.test.Demo.Generic;
import java.util.ArrayList;
import java.util.List;
public class GenericApplication {
public static void main(String[] args) {
List<A> l1 = new ArrayList<>();
List<B> l2 = new ArrayList<>();
List<C> l3 = new ArrayList<>();
show(l1);
show(l2);
// show(l3);编译时报错,只能是A以及A子类
}
public static <T> void show(List<? extends A> list){}
class A{}
class B extends A{}
class C {}
}
3、有下边界通配符
格式表达式:
<? super T> 表示传递类型必须是T以及T父类类型
展示代码
package com.chen.test.Demo.Generic;
import java.util.ArrayList;
import java.util.List;
public class GenericApplication {
public static void main(String[] args) {
List<A> l1 = new ArrayList<>();
List<B> l2 = new ArrayList<>();
List<C> l3 = new ArrayList<>();
show(l1);
// show(l2); 编译时报错,只能是A以及A的父类
//show(l3);编译时报错,只能是A以及A的父类
}
public static <T> void show(List<? super A> list){}
class A{}
class B extends A{}
class C {}
}
4、通配符弊端
package com.chen.test.Demo.Generic;
import java.util.ArrayList;
import java.util.List;
public class GenericApplication {
public static void main(String[] args) {
List<? extends A> l1 = new ArrayList<B>();
List<? super B> l2 = new ArrayList<A>();
// l1.add(new B());// 编译时报错
l2.add(new B());
}
// public static <T> void show(List<? super A> list){}
static class A{}
static class B extends A{}
}
5、通配符使用原则
如果频繁读取操作,适合使用extends上边界
如果频繁插入操作,适合使用super下边界
五、泛型中ETKVN含义
通过查询源码了解泛型参数类型具体含义:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}
public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {}
常见泛型参数类型:
T: 类中类型 Type
E: 集合中元素类型 element类型
V: 键值对Value(值)类型
K: 键值对Key(值)类型
N: 数字类型Number类型
?: 表示不确定的java类型
六、泛型实现原理
泛型本质是将数据类型参数化,通过擦除方式来实现,即在编译器在编译时候会对泛型 语法做一些类型转换操作。
定义一个泛型类和泛型成员变量,成员变量类型是T,T是泛型类型,具体是什么类型我们也不知道,它仅仅是限定类型的。
package com.chen.test.Demo.Generic;
public class GenericDemo<T>{
private T value;
}