泛型的好处
- 编译器的检查(类型安全)
- 减少了数据类型转换(消除了强制类型转换)
泛型类如果没有指定类型,就要按照Object类型来操作;
泛型类不支持基本数据类型,只支持包装类;在编译器的时候,我们会将泛型转换成Object,然后再使用成员的时候,在适当的时候将Object 转换为我们传进来的类型;基本数据类型 不是继承自Object,所以在编译的过程中,底层没办法将int转换成Object 类型来处理。
同一泛型类根据不同的数据类型创建的对象,本质上是同一类型。(内存地址一样)
package com.hrkj.main.common; /** * 泛型类 * @param <T> 泛型标识 - 类型参数形参 */ public class Generic<T> { // T 是外部使用类的时候来指定 private T key; public T getKey() { return key; } public void setKey(T key) { this.key = key; } @Override public String toString() { return "Generic{" + "key=" + key + '}'; } }
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>(); 注意:在java1.7之后,后面<>中的数据类型可以省略不写; 类名<具体的数据类型> 对象名 = new 类名<>();
泛型类,如果没有指定具体的数据类型,此时操作类型是Object
泛型类的类型参数只能是类类型,不能是基本数据类型
泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型(内存地址是一样的)
子类也是泛型类,子类和父类的泛型类型要一致;(子类泛型可以扩展)
class GhildGeneric<T> extends Generic<T> // 扩展为 class GhildGeneric<T,K,V> extends Generic<T> // 但是一定要存在T
子类不是泛型类,父类要明确泛型的数据类型
class GhildGeneric extends Generic<String>
实现类不是泛型类,接口需要明确数据类型
// 泛型接口 public interface Generate<T>{ T getKey(); }
// 接口实现类不是泛型类,需要明确实现泛型接口的数据类型 public class Apple implements Generate<String>{ @Override public String getKey(){ return "hello!"; } }
// 接口实现类是泛型类,要保证实现接口的泛型类泛型标识包含泛型接口的泛型标识(可以扩展) public class Pair<T,K> implements Generate<T>{ private T key; private K value; public Pair(T key,E value){ this.key = key; this.value = value; } @Override public T getKey(){ return key; } @Override public K getValue(){ return value; } }
实现类也是泛型类,实现类和接口的泛型类型要一致
语法结构
修饰符 <T,E,...> 返回值类型 方法名(形参列表){
方法体....
}
public
与返回值中间的<T>
非常重要,可以理解为声明此方法为泛型方法<T>
的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。<T>
表明该方法将使用泛型类型 T
此时才可以在方法中使用泛型类型T
// 定义一个泛型方法;list泛型参数;E泛型标识,具体类型由调用方法的时候来指定 public <E> E getProduct(ArrayList<E> list){ return list.get(1); }
即使定义的泛型方法的泛型标识和泛型类的泛型标识是一致的,在使用过程中,这两者是没有任何关系的,是独立存在的。
普通的成员方法如果采用了类的泛型,是不能被声明成静态方法的;如果是泛型方法,可以被声明成静态
// 成员方法使用了类的泛型,不能被定义成静态的 会编译报错 public static T getProduct(ArrayList<T> list){ return list.get(0); }
// 泛型方法声明成静态方法,是允许的 public static <T> T getProduct(ArrayList<T> list){ return list.get(0); }
//静态的泛型方法,采用多个泛型类型 public static <T,E,K> void printType(T t,E e,K k){ System.out.println(t+"\t" + t.getClass.getSimpleName()); System.out.println(e+"\t" + e.getClass.getSimpleName()); System.out.println(k+"\t" + k.getClass.getSimpleName()); } // 调用 测试 类名.printType(100,"java",true);
//泛型可变参数的定义 public static <E> void print(E... e){ for(int i=0;i<e.length;i++){ System.out.println(e[i]); } } // 调用 测试 类名.print(1,2,3,4,5,6,7,8);
泛型方法总结:
1: 泛型方法能使方法独立于类而产生变化
2: 如果static方法要使用泛型能力,就必须使其成为泛型方法
什么是类型通配符?
类型通配符一般是用 “?” 来代替具体的类型实参。
所以 类型通配符是类型实参,而不是类型形参。
类对象中的泛型类型不能用多态和继承的思想去理解;比如 Integer extends Number
申明的泛型是Number
时 不能传入Integer
。使用泛型通配符?
来定义就可以传入任意类型。
// 使用类型通配符来 配置,可以传入任意类型 public static void act(Person<?> person){ // 方法体 }