Bootstrap

Java基础 -- 04泛型<T>

目录

泛型概念:

泛型种类:

泛型接口

泛型类

泛型方法

        普通泛型方法

        静态泛型方法

    泛型构造函数

泛型进阶:

    泛型通配符

    泛型上界

    泛型下界

    泛型擦除

泛型注意事项

泛型的用法:

泛型的示例:

高阶篇


java <T> 泛型:死记硬背的

代码中有个  <T> 不要担心,只要编译能通过就OK!

泛型 <T> 定义

定义在类上      class MyClass <T> {}
定义在接口上  interface MyInterface <T> {}
定义在方法上  public <T> T myMethod( T arg) {}

泛型 <T> 运用

运用时,才能明确泛型T到底是个啥类型:
类实例化时      MyClass<String> obj = new MyClass< String>() ; // 明确 T 是 String
方法被调用时  obj.myMethod( "stringxx") ;                                   // 明确 T 是 String(入参arg)

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<>

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;