黑马程序员java学习<基础加强>—泛型

来源:互联网 发布:总后勤部职工数据库 编辑:程序博客网 时间:2024/06/05 17:33

泛型

一、泛型的出现:

1、泛型是在JDK1.5以后出现的新特性。泛型是用于解决安全问题的,是一个安全机制。

2、JDK1.5的集合类希望在定义集合时,明确表明你要向集合中装入那种类型的数据,无法加入指定类型以外的数据。

3、泛型是提供给javac编译器使用的可以限定集合中的输入类型说明的集合时,会去掉“类型”信息,使程序运行效率不受影响,对参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。

4、由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,如用反射得到集合,再调用add方法即可。

二、好处:

1、使用泛型集合,可将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象;这样就将运行时期出现的问题ClassCastException转移到了编译时期,方便与程序员解决问题,让运行时期问题减少,提高安全性。

2、当从集合中获取一个对象时,编译器也可知道这个对象的类型,不需要对对象进行强制转化,避免了强制转换的麻烦,这样更方便。

三、泛型格式:

     通过<>来定义要操作的引用数据类型

     如:TreeSet<String>     -----> 来定义要存入集合中的元素指定为String类型

四、泛型定义中的术语:

如:ArrayList<E>类和ArrayList<Integer>

1、ArrayList<E>整个称为泛型类型

2、ArrayList<E>中的E称为类型变量或类型参数

3、整个ArrayList<Integer>称为参数化类型

4、ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数

5、ArrayList<Integer>中的<>称为typeof

6、ArrayList称为原始类型

参数化:parametered,已经将参数变为实际类型的状态。

五、在使用java提供的对象时,何时写泛型?

     通常在集合框架中很常见,只要见到<>就要定义泛型,其实<>就是用来接收类型的,当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

 六、关于参数化类型的几点说明:

1、参数化类型与原始类型的兼容性

第一、参数化类型可引用一个原始类型的对象,编译只是报警告,能不能通过编译,是编译器说了算。

如:Collection<String> coll = new Date();

第二、原始类型可引用一个参数化类型的对象,编译报告警告

如:Collection coll = new Vector<String>();

原来的方法接受一个集合参数,新类型也要能传进去。

2、参数的类型不考虑类型参数的继承关系:

Vector<String> v = new Vector<Objec>();//错误的

不写Object没错,写了就是明知故犯

Vector<Objec> v = new Vector<String>();//错误的

3、在创建数组实例时,数组的元素不能使用参数化的类型

如:Vector<Integer> v[] = newVector<Integer>[10];//错误的

 

通配符

一、泛型中的通配符?

当传入的类型不确定时,可以使用通配符?

1、使用?通配符可引用其他各种类型化的类型,通配符的变量主要用作引用,也可调用与参数化无关的方法,但不能调用与参数化有关的方法。

2、可对通配符变量赋任意值:

如:Collection<?> coll ---> coll = newHashSet<Date>();

 

二、通配符的扩展-->泛型的限定:

对于一个范围内的一类事物,可以通过泛型限定的方式定义,有两种方式:

1、? extends E:可接收E类型或E类型的子类型;称之为上限。

如:Vector<? extends Number> x = newvector<Integer>();

2、? super E:可接收E类型或E类型的父类型;称之为下限。

如:Vector<? super Integer>x = newvector<Number>();

 

泛型方法

一、java中泛型方法的定义:

1、何时定义泛型方法:为了让不同方法可以操作不同的类型,而且类型不确定,那么就可以定义泛型方法

2、特殊之处:静态方法不可以访问类上定义的泛型,如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。

 

二、泛型方法的特点:

1、位置:用于放置泛型的类型参数的<>应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前,按照惯例,类型参数通常用单个大写字母表示。

2、只有引用类型才能作为泛型方法的实际参数

3、除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符。

4、普通方法、构造函数和静态方法中都可以使用泛型。

5、可以用类型变量表示异常,称之为参数化的异常,可用于方法的throws列表中,但是不能用于catch子句中。

6、在泛型中可同时有多个类型参数,在定义它们的<>中用逗号分开。

 

三、这个T和?有什么区别呢?

1、T限定了类型,传入什么类型即为什么类型,可以定义变量,接收赋值的内容。

2、?为通配符,也可以接收任意类型但是不可以定义变量。

但是这样定义,虽然提高了扩展性,可还是有一个局限性,就是不能使用其他类对象的特有方法。

 

泛型类

一、概述:

1、若类实例对象中多出要使用到同一泛型参数,即这些地方引用类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型。

2、何时定义泛型类:当类中要操作的引用数据类型不确定时,在早期定义Object来完成扩展,而现在定义泛型。

3、泛型类定义的泛型,在整个类中都有效,如果被方法调用,那么泛型类的对象要明确需要操作的具体类型后,所有要操作的类就已经固定了。

4、类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的。

 

 

 

二、语法格式:

1:定义:

public class GenerDao1<T>{  

    private T field;  

    public void save(T obj){}  

    public T getByteId(int Id){}  

2、举例:

扩展:Dao:Data Access Object,数据访问对象。

对其操作:crud即增上删改查

c:creat,创建、增加;     r:read,读取、查询;

u:update,更新、修改    d:delete,删除。

对javaEE的理解:13种技术。简单说就是对数据库的增删改查。

写Dao类有五个基本方法:增删改查,其中查包含查单个和对同类型集合的查询,如同性别或同地区的集合获取。

}  

三、注意:

1、在对泛型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。

2、当一个变量被声明为参数时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用,因为静态成员是被所有参数化的类共享的,所以静态成员不应该有类级别的类型参数。

 

四、示例:

class GenerticTest  {        public static void main(String[] args) {            //创建泛型类对象            GenClass<Worker> g = new GenClass<Worker> ();            g.setTT(new Worker());            Worker w =  g.getTT();            g.showC(w);            System.out.println("----------------------");                //泛型方法测试                GenMethod<String> g1 = new GenMethod<String>();                GenMethod.showS("SSS");                g1.show("sesf");                g1.print("heheh");                g1.printY(new Integer(5));                System.out.println("------------------------");                //泛型接口测试                GenInter g2 = new GenInter();                g2.show("haha");                System.out.println("Hello World!");                GenImpl<Integer> g3 = new GenImpl<Integer>();                g3.show(new Integer(95));            }        }       //泛型类       class GenClass<TT>  {            //定义私有属性            private TT t;            //定义公共设置方法,设置属性            public void setTT(TT t) {                this.t = t;            }            //定义公共访问方法,访问属性            public TT getTT() {                return t;            }           //定义方法            public void showC(TT t) {                System.out.println("GenClass show:" + t);            }        }        //创建Worker类,作为类型传入泛型类中        class Worker {}        //泛型方法        class GenMethod<T> {            //静态的泛型方法            public static <S> void showS(S s) {              System.out.println("static show:" + s);            }            //非静态泛型方法            public void show(T t) {                System.out.println("未指定T show:" + t);            }            public void print(T t) {                System.out.println("指定T print:" + t);            }            //指定接受其他类型的泛型方法            public <Y> void printY(Y y) {                System.out.println("和类指定的不同,为Y print:" + y);            }         }        //泛型接口                interface Inter<T> {            void show(T t);        }        //一般类实现泛型接口        class GenInter implements Inter<String> {            public void show(String s) {                System.out.println("接口 show:" + s);            }        }        //泛型类实现泛型接口        class GenImpl<T> implements Inter<T> {            public void show(T t) {                System.out.println("类接收类型不确定的实现接口 show:" + t);    }      


 

 

参数的类型推断

一、概述:

1、定义:编译器判断泛型方法的实际参数的过程,称之为类型推断。

2、类型推断是相对于直觉推断的,其实现方法是一种非常复杂的过程。

二、类型推断的具体规则:

根据调用泛型方法时,实际传递的参数类型或返回值的类型来推断。

1、当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时,该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时,传递的参数类型或返回值来决定泛型参数的类型,如:

swap(newString[3],1,2)

 ---> static <E> void swap(E[] a, inti, int j);

2、当某个类型变量在某个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时,这多处的实际应用类型都对应同一种类型来表示,这很容易凭感觉推断出来:

add(3,5)

---> static<T> T add(T a,T b);

3、若对应了不同类型,且没有使用返回值,这是取多个参数中的最大交集类型,如下面的对应类型Number,编译没问题,但是运行会出错:

fill(new Integer[3],3.5f)

---> static<T> void fill(T[] a,T v);

4、若对应了不同类型,且使用了返回值,这时候优先考虑返回值类型,如下面语句实际对应的类型就是Integer了,编译将报错,将变量x类型改为float,对此eclipse报错提示,接着再将变量x类型改为Number,则就没了错误:

int x = add(3,3.5f)

---> static<T> T add(T a,T b);

5、参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没问题,而第二种情况则会根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:

copy(newInteger[5],new String[5]);

---> static<T> T copy(T[] a,T[] b);

 

 

 

 

 

 

 

 

 

 

 

 

原创粉丝点击