第一 泛型概述
一、概述:
1、泛型是在JDK1.5出现的新特性。泛型是用于解决安全问题的,是一个安全机制。
2、JDK1.5的集合类希望在定义集合时,明确表明你要向集合中装入那种类型的数据,无法加入指定类型以外的数据。
3、泛型是提供给javac编译器使用的可以限定集合中的输入类型说明的集合时,会去掉“类型”信息,使程序运行效率不受影响,对参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。
4、由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,如用反射得到集合,再调用add方法即可。
二、好处:
1、使用泛型集合,可将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象;这样就将运行时期出现的问题ClassCastException转移到了编译时期,方便与程序员解决问题,让运行时期问题减少,提高安全性。
2、当从集合中获取一个对象时,编译器也可知道这个对象的类型,不需要对对象进行强制转化,避免了强制转换的麻烦,这样更方便。
三、泛型格式:
通过<>来定义要操作的引用数据类型如:ArrayList<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];//错误的
4、 Collection<Object> coll = new ArrayList<String>();编译时不能通过的 Collection col = new ArrayList<String>();Collection<Object> co = col;编译是可以通过的
public class Demo {
@Test
public void test() throws Exception {
ArrayList<String> arrss = new ArrayList<String>();
ArrayList<Integer> arrsi = new ArrayList<Integer>();
// 泛型存储时时按照指定类型进行存储的,但是编译完后就会去掉类型被存成了一份字节码
// 所以不能获取到其类型,泛型是给编译器看的
System.out.println(arrss.getClass() == arrsi.getClass());
//用反射方式越过编译器,对其直接进行字节码的设值,也证明了上面注释的内容
ArrayList<Integer> arrsi2 = new ArrayList<Integer>();
arrsi2.getClass().getMethod("add", Object.class).invoke(arrsi2, "abc");
System.out.println(arrsi2.get(0));
Collection col = new ArrayList<String>();
Collection<Object> co = col;//这种是可以通过的
// Collection<Object> coll = new ArrayList<String>();//不能通过
}
@Test
public void test_2(){
swap(new String[]{"ab","cd","ee"},1,2);
// swap(new int[]{1,5,4,9,3},1,2);//这种不行,泛型只能接收类类型的变量
}
//交换任何类型的数组中的两个元素的位置
public static <T> void swap(T[] a,int i,int j){//泛型放在返回值之前
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
第二 泛型的通配符
一、泛型中的通配符:当传入的类型不确定时,可以使用通配符?表示
1、使用?通配符可引用其他各种类型化的类型,通配符的变量主要用作引用,这种形式因为类型不确定,所以没办法调用到具体类型中的方法。
2、可对通配符变量赋任意值:
public static void printObj(Collection<?> coll){
//coll.add(1);是错误的,如果传入的是具体类型,通配符是没有办法确定具体类型的,所以不能操作具体类型
for(Object obj : coll){
System.out.println(obj);
}
}
二、通配符的扩展-->泛型的限定:
对于一个范围内的一类事物,可以通过泛型限定的方式定义,有两种方式:
1、? extends E:可接收E类型或E类型的子类型;称之为上限。
如:Vector<? extends Number> x = newvector<Integer>();
2、? super E:可接收E类型或E类型的父类型;称之为下限。
如:Vector<? super Integer>x = newvector<Number>();
/*
* 自定义泛型
* 类中操作的引用数据类型不确定时定义泛型
* 早期定义object来完成扩张,现在定义泛型来完成扩展
*
* ?通配符也可以理解为占位符
* <? extends E>可以接受E类型或者E的子类型,上限
* <? super E>可以接受E类型或者E的父类型,下限
*
*/
public class GenericDemo {
public static void main(String[] args) {
/*
* Tool<Pen> t = new Tool<Pen>(); t.setObject(new Pen()); Pen p =
* t.getObject();
*/
/*
* Tool t = new Tool(); t.show("12213"); t.show("haha"); t.print(4);
*/
/*
* Tool<String> t = new Tool<String>(); t.show("sjjh"); t.print(15151);
* Tool.method("xscsa");
*/
ArrayList<Book> listb = new ArrayList<Book>();
listb.add(new Book("Chinese"));
listb.add(new Book("Math"));
listb.add(new Book("English"));
ArrayList<JavaBook> listjb = new ArrayList<JavaBook>();
listjb.add(new JavaBook("java01"));
listjb.add(new JavaBook("java02"));
listjb.add(new JavaBook("java03"));
printColl(listb);
printColl(listjb);
}
public static void printColl(Collection<? extends Book> c) {
for (Iterator<? extends Book> it = c.iterator(); it.hasNext();) {
//? extends E
System.out.println(it.next().getName());
//? super E
// System.out.println(it.next());
/*//Collection<?> c 这种形式因为类型不确定,所以没办法调用到具体类型中的方法
System.out.println(it.next().toString());*/
}
}
}
/*
* //自定义泛型类 class Tool<T> {// T type private T t;
*
* public void setObject(T t) { this.t = t; }
*
* public T getObject() { return t; } }
*/
/*
* // 自定义泛型方法 class Tool { public <T> void show(T t) {//<T>放在关键字后面,返回值类型前面
* System.out.println("show: " + t); }
*
* public <E> void print(E e) { System.out.println("show: " + e); } }
*/
/*
* class Tool<T> { public void show(T t) { System.out.println(t); }
*
* public <E> void print(E e) { System.out.println(e); }
*
* public static <W> void method(W w) {//
* 静态方法不能引用类上的泛型,因为静态先加载,而类上的泛型是随着对象的建立而明确的 System.out.println(w); } }
*
* class Pen implements InterG<String>{
*
* @Override public void method(String t) {
*
* }
*
* }
*
* interface InterG<T>{ public abstract void method(T t); }
*
* class InterIm <T> implements InterG<T>{
*
* @Override public void method(T t) {
*
* }
*
* }
*/
class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class JavaBook extends Book {
JavaBook(String name) {
super(name);
}
}
/*
* ? super E
* 应用
*/
public class GenericTest {
public static void main(String[] args) {
TreeSet<JBook> tsjb = new TreeSet<JBook>(new MyComp());
tsjb.add(new JBook("java02"));
tsjb.add(new JBook("java05"));
tsjb.add(new JBook("java03"));
TreeSet<CBook> tscb = new TreeSet<CBook>(new MyComp());
tscb.add(new CBook("c++07"));
tscb.add(new CBook("c++02"));
tscb.add(new CBook("c++03"));
// MyBook[] my = {new MyBook("jk"),new MyBook("113")};
// for(int x=0; x<my.length; x++){
// System.out.println("bookarray: " + my[x].getName());
// }
MyIterator(tsjb);
MyIterator(tscb);
}
public static void MyIterator(Collection<? extends MyBook> c) {
for (Iterator<? extends MyBook> it = c.iterator(); it.hasNext();) {
System.out.println(it.next().getName());
}
}
}
class MyComp implements Comparator<MyBook> {// ? super
// E,指定父类,可以用于比较其子类。但是不能使用子类特有方法
@Override
public int compare(MyBook o1, MyBook o2) {
return o1.getName().compareTo(o2.getName());
}
}
class MyBook {
private String name;
MyBook(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class JBook extends MyBook {
JBook(String name) {
super(name);
}
}
class CBook extends MyBook {
CBook(String name) {
super(name);
}
}
第三 自定义泛型方法
一、自定义泛型方法:
1、何时定义泛型方法:为了让同一方法可以操作不同的类型,而且类型不确定,那么就可以定义泛型方法
2、特殊之处:静态方法不可以访问类上定义的泛型,如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。
二、泛型方法的特点:
1、位置:用于放置泛型的类型参数的<>应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前,按照规范,类型参数通常用单个大写字母表示,如E、T。
2、只有引用类型才能作为泛型方法的实际参数
3、除了在应用泛型时可以使用extends和super限定符,在定义泛型时也可以使用extends和super限定符。
4、普通方法、构造函数和静态方法中都可以使用泛型。
5、可以用泛型变量表示异常,称之为参数化的异常,可用于方法的throws列表中,但是不能用于catch子句中。
6、在泛型中可同时有多个类型参数,在定义它们的<>中用逗号分开,如Map<K, V>。
三、这个T和?有什么区别呢?
1、T限定了类型,传入什么类型即为什么类型,可以定义变量,接收赋值的内容。
2、?为通配符,也可以接收任意类型但是不可以定义变量。
但是这样定义,虽然提高了扩展性,可还是有一个局限性,就是不能使用其他类对象的特有方法。
3、总结:通配符方案要比泛型方法更有效,当一个类型变量用来表达两个参数之间或参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用,而不是仅在签名的时候使用,才需要使用泛型方法。
第四 自定义泛型类
一、概述:
1、若类实例对象中多处要使用到同一泛型参数,即这些地方引用类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型。
2、何时定义泛型类:当类中要操作的引用数据类型不确定时,在早期定义Object来完成扩展,而现在定义泛型。
3、泛型类定义的泛型,在整个类中都有效,如果被方法调用,那么泛型类的对象要明确需要操作的具体类型后,所有要操作的类就已经固定了。
4、类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的。
二、语法格式:
public class GenerDao1<T>{
private T field;
public void save(T obj){}
public T getByteId(int Id){}
}
三、泛型类和泛型方法同时定义:
当整个类要操作的类型确定了类中大部分方法要操作的类型,但是某些方法操作的类型与类上定义的类型不一致就可以同时定义泛型方法和泛型类
public class GenerDao1<T>{
private T field;
public <E>void save(E obj){}
public T getByteId(int Id){}
}
示例:
/*
* 泛型类只要类型一确定,该类型就在整个类中产生作用
*/
public class GenericDemo {
public static void main(String[] args) {
Utils<Worker> utils = new Utils<Worker>();
utils.setObject(new Worker());
Worker worker = utils.getObject();
}
}
class Utils<T> {
private T t;
public void setObject(T t) {
this.t = t;
}
public T getObject() {
return t;
}
}
class Worker {
}
// ---------------------------------------------------------------------------------
class Demo {
public <T> void show(T t) {
System.out.println("show : " + t);
}
public <T> void print(T t) {
System.out.println("print : " + t);
}
}
class GenericDemo_m {
public static void main(String[] args) {
Demo demo = new Demo();
demo.show("haha");
demo.show(new Integer(12));
demo.print("heihei");
}
}
// ---------------------------------------------------------------------------------
class Demo1<T> {
public void show(T t) {
System.out.println("show : " + t);
}
public <Q> void print(Q q) {
System.out.println("print : " + q);
}
public static <W> void sta_show(W w) {
System.out.println("sta_show : " + w);
}
}
class GenericDemo_m1 {
public static void main(String[] args) {
Demo1 demo = new Demo1();
demo.show("haha");
demo.show(new Integer(12));
demo.print("heihei");
Demo1.sta_show("jhlkl");
}
}
// ------------------------------------------------------------------------------
interface Inter<T> {
void show(T t);
}
/*
* class InteImp implements Inter<String> { public void show(String s){
* System.out.println(s); } }
*/
class IntImp<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
class Dem {
public static void main(String[] args) {
IntImp<Integer> inter = new IntImp<Integer>();
inter.show(1546);
}
}
// ------------------------------------------------------------------
class Demo6 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("aaaaaaaaa");
arrayList.add("aaaaaaaaa");
arrayList.add("aaaaaaaaa");
ArrayList<Integer> arr_in = new ArrayList<Integer>();
arr_in.add(12);
arr_in.add(34);
arr_in.add(56);
printColl(arrayList);
printColl(arr_in);
}
public static void printColl(ArrayList<?> al) {
Iterator<?> iterator = al.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
// ---------------------------------------------------------------------------------
class Demo7 {
public static void main(String[] args) {
ArrayList<Person> ap = new ArrayList<Person>();
ap.add(new Person("aaaa"));
ap.add(new Person("aaaa"));
ap.add(new Person("aaaa"));
ap.add(new Person("aaaa"));
printColl(ap);
ArrayList<Student> as = new ArrayList<Student>();
as.add(new Student("12121"));
as.add(new Student("12121"));
as.add(new Student("12121"));
as.add(new Student("12121"));
printColl(as);
}
public static void printColl(ArrayList<? extends Person> al) {
Iterator<? extends Person> iterator = al.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getName());
}
}
}
class Person {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Student extends Person {
Student(String name) {
super(name);
}
}
第五 扩展
一、概述: 1、由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据。
2、通过变量本身是没有办法得到泛型中的类型的,但是可以将带泛型的参数作为方法的参数列表,然后再通过方法获取到参数的类型。
@Test
public void paraAsGeneric() throws Exception{
//想要得到泛型中实际类型,直接通过泛型类是不行的,因为编译完之后就会被去参数化
// ArrayList<Date> arr = new ArrayList<Date>();
/*
* 所以就通过反射类中的方法,在操作方法的对象得到其具体的类型
*/
Method applyMethod = Demo.class.getMethod("apply", ArrayList.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType type = (ParameterizedType) types[0];
System.out.println(type.getRawType());
System.out.println(type.getActualTypeArguments()[0]);
}
public void apply(ArrayList<Date> arr){
}
}