目录
java <T> 泛型:死记硬背的
泛型 <T> 定义
泛型 <T> 运用
T 类型 的私有数据成员,要求类定义时一定要携带<T>,这样才能编译通过:
public class Test {
private T name; // 编译报错
}
public class Test<T> {
private T name; // 编译通过
}
泛型概念:
没有JDK5问世以前的老代码:
- 先看第1个小示例:Integer类型数值求和a+b,Long类型数值求和a+b
public class GenericTest1
{
public int sum(int a, int b){
return a+b;
}
public long sum(long a, long b){
return a+b;
}
}
我们来分析一下第1个小示例,会发现两个方法除了类型不一样,几乎长得是一摸一样,像双包胎。试问?如果我们还有byte类型的求和,short类型的求和,float类型的求和。。。我们是不是要繁琐的机械的重写很多份这样的方法,这些方法仅仅参数类型和返回类型不一样而已,其它都一样。 那么,我们能不能像模具那样来写方法呢?比如:
public <T> T sum(T a, T b){
return a+b;
}
这里的T就是个模具,整个方法就是模具式的方法。当你想把T当成byte就当成byte,想把T当成int就当成int,...随心所欲,看你真正使用时想给模具T什么样的真实身份。就是一个模具方法,足矣代表byte类型参数的求和,也能代表short类型的求和,也能代表int类型的求和,等等...恭喜你,当你对此有个认识的时候,你就开始慢慢摸进了泛型方法编码的领域
- 再看第2个小示例:从ArrayList集合类中取出Integer对象
public class GenericTest2 {
private ArrayList arr = new ArrayList();
public void setIntValue(Integer value){
arr.add(value);
}
public void setStrValue(String value){
arr.add(value);
}
public Integer getIntValue(int idx){
return (Integer)arr.get(idx);
}
}
我们来分析一下第2个小示例,会发现getIntValue方法的内部,arr.get(i)每次都要进行类型强转(Integer)arr.get(i);
注意:这里是有风险的,因为既可以往arr中存放Integer类型的元素,也可以往arr中存放String类型的元素,所以取出时的类型强转有可能我们取出的元素想强转为Integer,但该元素却是String类型的,此时运行程序时就会报ClassCastException类型转换异常。那么,试问?如果我限定了能放入arr中的只能是Integer类型的元素的话,当我从arr中取出元素时,根本不用类型强制转换,铁定取出的元素是Integer类型的,由此我们可以这样来定义一个模具集合类,请看第2个小示例的改造:
public class GenericTest2 {
private ArrayList<T> arr = new ArrayList<T>();
public void setValue(T value){
arr.add(value);
}
public T getIntValue(int idx){
return arr.get(idx);
}
}
这里的T就是个模具,整个ArrayList集合类就是模具式的类。当你想把T当成Integer就当成Integer,这样取出的元素铁定是Integer类型的,当你想把T当成String就当成String,这样取出的元素铁定是String类型的...随心所欲,看你真正使用这个模具时想给模具T什么样的真实身份。足矣明确取出的元素类型就是我当初放入时的元素类型,并不需要取出时进行类型强制转换...恭喜你,当你对此有个认识的时候,你就开始慢慢摸进了泛型类编码的领域
JDK5的到来,为我们提供了模具思想的API,只不过JDK5不叫模具,而是叫做泛型。
泛型的定义:参数化类型,也就是说类型是参数化(/模具)的,Java编译后才知道这个类型到底是个啥,类型T仅仅是个模具。泛型的表现形式<T>有尖括号括起来T的整体,单单的一个T并不符合泛型的定义,而仅仅是利用了T而已。创建类型安全的代码,从而在编译时能够捕获类型不匹配错误,这是泛型的一个关键优势,也能体现JDK5引入泛型的意义。
泛型定义时:模具<T> 或 有边界的 <T extends superclass> <T super subclass>
泛型使用时:
// 泛型<T> 只能用于定义
// 泛型<T> 使用:new T(); 非法哦!
// 需要明确T的类型 (比如: MyCls<Son>)
// 或者 使用?通配符 (比如: MyCls<?>)
// 或者 使用?有界通配符 (比如: MyCls<? extends Father>)
// 或者 仅仅用来做obj的类型强制转换 (比如:(T)obj; )
泛型的思想:定义时模具,使用时明确类型,本质:类类型的校验,是否还要强制转换
泛型种类:有 <T> 是泛型的前提条件
泛型接口
interface MyInterface<T> {
// 实现该接口的类,可以仅限定T,class MyClass<T> implements MyInterface<T>{ }
// 实现该接口的类,可以扩容,class MyClass<T,K> implements MyInterface<T>{ }
}
public interface MyInterface<T> {
// 仅仅使用T的普通方法
T xxxMethod(); // 返回值类型T
T xxxMethod(T t); // 参数类型T,返回值类型T
void yyyMethod(T t); // 参数类型T,无返回值(这里举例无返回值,其实返回值是啥类型都行)
// <T>的泛型方法
<T> T aaaMethod();
<T> T aaaMethod(T t);
<T> void bbbMethod(T t);
}
public abstract class MyClass<T> implements MyInterface<T>{} // OK
public abstract class MyClass<T,K> implements MyInterface<T>{} // OK
泛型类
class MyClass<T> {
// 为什么泛型类?目的:类内部的数据成员是泛型变量的成员,例如:private T a;
// 该类被实例化时,已经明确了T到底是啥类型,所以编译时能做到类型检查和泛型擦除
}
// 使用时才明确泛型类的MyClass<T>的T到底是个啥
MyClass<String> obj = new MyClass<>