一、什么是泛型
Java的泛型 (generics) 是在JDK5中推出的新概念,在泛型推出之前,程序员需要构建一个元素为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则会发生ClassCastException (类转换异常)
而泛型提供了编译时类型安全监测机制,允许我们在编译时检测到非法的类型数据结构,它的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数 (形参),
以ArrayList举例子来说,通过查看ArrayList源码可以看到,ArrayList中可以存放任意的类型是因为有一个泛型<E>,当new一个ArrayList并在泛型中放任意的类型之后此时这个ArrayList就只能存泛型中存放的这个类型的对象,这就是泛型的作用
泛型的好处就在于可以让类型的存放更安全并且可以避免强制类型的转换不会报ClassCastException
二、泛型类
1、语法定义
class 类名<泛型标识, 泛型标识, ...>{
private 泛型标识 变量名;
.....
}
使用语法
类名<具体数据类型> 对象名 = new 类名<具体数据类型>();
java1.7之后,后面的<>中的具体的数据类型可以省略不写
类名<具体的数据类型> 对象名 = new 类名<>();
注意事项:
泛型类,如果没有指定具体的数据类型,此时操作类型是Object
<具体数据类型> 泛型中只能存放包装类
泛型类在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
例:
public class Test1<T> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
public class MainMethod {
public static void main(String[] args) {
Test1<String> test1 = new Test1<>();
test1.setKey("abc");
System.out.println("test1存的key为:" + test1.getKey());
System.out.println("test1的类型为:" + test1.getClass().getSimpleName());
System.out.println("test1存的key的类型为:" + test1.getKey().getClass().getSimpleName());
}
}
同一泛型类,根据不同的数据类型创建的对象,本质上是同一类型
public class MainMethod {
public static void main(String[] args) {
Test1<String> test1 = new Test1<>();
test1.setKey("abc");
System.out.println("test1的类型为:" + test1.getClass());
Test1<Integer> test2 = new Test1<>();
test2.setKey(100);
System.out.println("test2的类型为:" + test2.getClass());
System.out.println(test1.getClass() == test2.getClass());
}
}
多个泛型
public class Test1<T,V> {
private T key;
private V value;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
public class MainMethod {
public static void main(String[] args) {
Test1<String,Integer> test1 = new Test1<>();
test1.setKey("number");
test1.setValue(100);
System.out.println(test1.getKey() + ":" + test1.getValue());
}
}
2、常用的泛型标识: T、E、K、V、?
T (type) 表示具体的一个java类型 常用在定义单个对象或者单个方法时
E (element) 代表元素 常在集合中使用
K V 分别代表java键值中的Key和Value 常用在类似于map的集合中
?表示不确定的 java 类型 常在上下限中使用
三、泛型类派生子类
1、语法定义
子类也是泛型类,子类和父类的泛型类型要一致
class Children<T> extends Father<T>
子类不是泛型类,父类要明确泛型的数据类型
class Children extends Father<具体类型>
2、使用场景
可以看出使用instanceof之后调用的是children的属性
要是父类.属性调用的就是父类的
四、泛型接口
1、语法定义
interface 接口名<泛型标识,泛型标识, ...>{
泛型标识 方法名();
.....
}
2、泛型接口的使用
实现类不是泛型类,接口要明确数据类型
实现类也是泛型类,实现类和接口的泛型类型要一致
例:
public interface InMethod<T> {
T getKey(T t);
}
//如果此时实现接口后泛型中不指定具体类型那么重新接口的方法和派生子类一样是Object
public class claMethod implements InMethod{
@Override
public Object getKey(Object o) {
return null;
}
}
//如果此时实现接口后泛型中指定具体的类型那么重写接口方法之后返回的就是定义的类型
public class claMethod implements InMethod<Integer>{
@Override
public Integer getKey(Integer integer) {
return null;
}
}
实现接口之后重写一下接口中的方法
public class ClaMethod implements InMethod<Integer>{
@Override
public Integer getKey(Integer integer) {
return integer;
}
}
public class MainMethod {
public static void main(String[] args) {
ClaMethod claMethod = new ClaMethod();
Integer num = claMethod.getKey(100);
System.out.println(num.getClass().getSimpleName());
}
}
类实现接口并使用了接口中的方法后因为类没有定义泛型所以使用的类型是接口的类型
//接口中的泛型是实现类中泛型的其中一个就可以
public class ClaMethod<T,K,E> implements InMethod<K>{
@Override
public K getKey(K k) {
return null;
}
}
五、泛型方法
1、语法定义
修饰符 <T, E, ...> 返回值类型 方法名(形参列表) {
方法体 ...
}
【注意】
· public 与 返回值中间<T> 非常重要,可以理解为声明此方法为泛型方法。
· 只有声明了<T>的方法才是泛型方法,泛型类中 使用了泛型的成员方法并不是泛型方法
· <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T
· 与泛型类的定义一样,此处T可以随便写为任意标识,如 T、E、K、V等形式的参数常用于表示 泛型
例:
public class ClaMethod<T,K,E>{
public <T> T getGeneric(T t){
return t;
}
}
public class MainMethod {
public static void main(String[] args) {
ClaMethod claMethod = new ClaMethod();
String str = "abc";
System.out.println(claMethod.getGeneric(str).getClass().getSimpleName());
}
}
//要是调用的不是void方法 可以传多个类型 return的和上边返回的类型要一致
public <T,K> K GG(K k){
return k;
}
2、静态的泛型方法
public class ClaMethod<T,K,E>{
//静态的泛型方法采用多个泛型类型
public static <T,K,E> void getGenericType(T t,K k){
System.out.println(t+"\t"+t.getClass().getSimpleName());
System.out.println(k+"\t"+k.getClass().getSimpleName());
}
}
public class MainMethod {
public static void main(String[] args) {
//static使用类名.方法名
ClaMethod.getGenericType("abc",100);
}
}
3、泛型方法与可变参数
public <E> void print(E... e){
for(E date : e){
System.out.println(date);
}
}
public class ClaMethod{
public<E> void printE(E... e){
for (E data : e) {
System.out.println(data);
}
}
}
public class MainMethod {
public static void main(String[] args) {
ClaMethod claMethod = new ClaMethod();
claMethod.printE("a","b","c");
}
}
4、泛型方法和泛型类的区别
泛型类,是在实例化类的时候指明泛型的具体类型
泛型方法,是在调用方法的时候指明泛型的具体类型
5、泛型方法总结
泛型方法能使方法独立于类而产生变化
泛型可以使用多个,要是没有void 就return其中一个泛型,要是有void那么就正常输出就好
静态的泛型方法和普通泛型方法的区别是 静态的泛型方法是在泛型前多加一个static
六、类型通配符
1、定义
类型通配符一般是使用 "?" 代替具体的类型实参,所以,类型通配符是类型实参,而不是类型形参
一般和上下限进行配合使用
2、示例
public class ListMethod {
private String listKey;
public ListMethod(String listKey) {
this.listKey = listKey;
}
@Override
public String toString() {
return "ListMethod{" +
"listKey='" + listKey + '\'' +
'}';
}
}
public class MainMethod {
public static void main(String[] args) {
ListMethod listMethod1 = new ListMethod("a");
ListMethod listMethod2 = new ListMethod("b");
ListMethod listMethod3 = new ListMethod("c");
ArrayList<ListMethod> list = new ArrayList<>();
list.add(listMethod1);
list.add(listMethod2);
list.add(listMethod3);
MainMethod.getMethod(list);
}
public static void getMethod(List<?> list){
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}