----------android培训、Java培训、期待与您交流!----------
总结一下张老师和毕老师所讲解的泛型
在详细介绍之前先大概说一下泛型的一些由来和一些基本信息
泛型:JDK 1.5版本以后出现的心特性,用于解决安全问题,是一个类型安全机制。
好处:
1,将运行时期出现的问题ClassCastException,转移到了编译时期,方便程序员解决问题,让运行时期问题减少,安全。
2,避免了强制转换的麻烦
1,将运行时期出现的问题ClassCastException,转移到了编译时期,方便程序员解决问题,让运行时期问题减少,安全。
2,避免了强制转换的麻烦
泛型的格式:
通过<>来定义要操作的引用数据类型
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要加到<>就要定义泛型,其实<>就是用来接收类型的, 当使用集合时,将集合中要存储的的数据类型作为参数传递到<>中即可
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要加到<>就要定义泛型,其实<>就是用来接收类型的, 当使用集合时,将集合中要存储的的数据类型作为参数传递到<>中即可
泛型类
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来扩展
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来扩展
泛型类定义的泛型,在整个类中有效,如果被方法,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
特殊之处:
静态方法不可以访问类上定义的泛型,如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
特殊之处:
静态方法不可以访问类上定义的泛型,如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上
下面详细介绍泛型。
一、java泛型的概念
泛型,英文名字是generic programming,或generic type或许译成“通用编程”更合适。也有人叫它参数化的类,“类的类”,或“元类metaclass”,像类一样,泛型也是一种数据类型。泛型比类更抽象,但是泛型不属于面向对象,它是面向对象的补充和发展
Java 泛型的参数只可以代表类,不能代表个别对象。由于 Java 泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型。Java 编译器在编译泛型时会自动加入类型转换的编码,故运行速度不会因为使用泛型而加快。Java 允许对个别泛型的类型参数进行约束,包括以下两种形式(假设 T 是泛型的类型参数,C 是一般类、泛类,或是泛型的类型参数):T 实现接口 I 。T 是 C ,或继承自 C 。一个泛型类不能实现Throwable接口。
jdk 1.5以前的集合类中存在什么问题
Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
int i = (Integer) collection.get(1);//编译要强制类型转换且运行时出错!
Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据
ArrayList<Integer> collection2 = new ArrayList<Integer>();
collection2.add(1);
/*collection2.add(1L);
collection2.add(“abc”);*///这两行代码编译时就报告了语法错误
int i2 = collection2.get(0);//不需要再进行类型转换
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
二、泛型中的特点与通配符介绍
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告
例如, Collection<String> c = new Vector();//可不可以,不就是编译器一句话的事吗?
原始类型可以引用一个参数化类型的对象,编译报告警告
原始类型可以引用一个参数化类型的对象,编译报告警告
例如, Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去
参数化类型不考虑类型参数的继承关系:
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,可以认为是与以前的版本兼容。写了就是明知故犯。 错误(这个需要仔细理解)
Vector<Object> v = new Vector<String>(); //这个就更不对了,不要认为object是根类就可以,后面<String>明确说明只可以装 String类型,这和前面会产生矛盾。明确记住参数化类型不考虑继承!
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,
例如,下面语句有错误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];//这个张老师说他也没搞明白,不需要严格记忆,在实践中 ,遇到 自然会报错,然后修改
思考题:下面的代码会报错误吗?
Vector<Integer> vectorList[] = new Vector<Integer>[10];//这个张老师说他也没搞明白,不需要严格记忆,在实践中 ,遇到 自然会报错,然后修改
思考题:下面的代码会报错误吗?
Vector v1 = new Vector<String>(); //这个要注意,参数化类型给原始类型不会报错,
Vector<Object> v = v1;//一开始感觉是错的,但是张老师说编译器是一个严格按照语法检查的工具,
是不会考虑运行时的效果,我们分开看两句,编译到第二句时会认为v1是原始类型,这样就不会报错了
编译器是一行一行扫描,不要按代码运行!
问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:
总结:
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:
public static void printCollection(Collection<Object> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
/* cols.add("string");//没错
cols = new HashSet<Date>();//会报告错误!*/这句是错的,上面写的是object,他们之间不考虑继承关系,所以是矛盾的
}
正确方式:
public static void printCollection(Collection<?> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String与例1一同理解,add方法与参数类型有关系
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet<Date>();//注意:这句是对的,体现了泛型中通配符所代表的是引用,他可以引用为其让他任何类型的参数(是引用而不是直接使用与参数有关的方法)
}
总结:
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法
示例:
public static void printCollection(Collection<?> xollection){
collection.add(1);//这句会报错调用了与参数有关的方法
collection.size();//这句是正确的与类型无关
}
}
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();// 必须是number的子类 、
错误:Vector<? extends Number> x = new Vector<String>();//不是子类错误
限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。
class Info<T>{
private T var ; // 定义泛型变量
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
public String toString(){ // 直接打印
return this.var.toString() ;
}
};
public class GenericsDemo14{
public static void main(String args[]){
Info<String> i = new Info<String>() ; // 使用String为泛型类型
i.setVar("it") ; // 设置内容
fun(i) ;
}
public static void fun(Info<?> temp){ // 可以接收任意的泛型对象
System.out.println("内容:" + temp) ;
}
};
泛型定义在接口上。
package day15;
/*
泛型定义在接口上。
*/
interface Inter<T>
{
void show(T t);
}
/*
class InterImpl implements Inter<String>
{
public void show(String t)
{
System.out.println("show:"+t);
}
}
*/
class InterImpl<T> implements Inter<T>
{
public void show(T t)
{
System.out.println("show:"+t);
}
}
class GenericDemo4
{
public static void main(String[] args)
{
InterImpl<Integer>i=new InterImpl<Integer>();
i.show(4);
//InterImpl i=new InterImpl();
//i.show("haha ");
}
}
通配符的理解以及泛型的限定
package day15;
/*
?通配符。也可以理解为占位符
泛型的限定:
? extends E:可以接收E类型或者E的子类型。上限
?super E:可以接收E类型或者E的父类型。下限
*/
import java.util.*;
class GenericDemo5
{
public static void main(String[] args)
{
/*
ArrayList<String>al=new ArrayList<String>();
al.add("abs01");
al.add("abs02");
al.add("abs03");
al.add("abs04");
ArrayList<Integer>al1=new ArrayList<Integer>();
al1.add(3);
al1.add(5);
al1.add(6);
al1.add(8);
printColl(al);
printColl(al1);
*/
ArrayList<Person>al=new ArrayList<Person>();
al.add(new Person("abc1"));
al.add(new Person("abc2"));
al.add(new Person("abc3"));
//printColl(al);
ArrayList<Student_3>al1=new ArrayList<Student_3>();
al1.add(new Student_3("abc--1"));
al1.add(new Student_3("abc--2"));
al1.add(new Student_3("abc--3"));
printColl(al1);//ArrayList<Student_3>al1=new ArrayList<Person>; error
}
public static void printColl(ArrayList<? extends Person>al)
{
Iterator<? extends Person>it=al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
/*
public static void printColl(ArrayList<?>al)
{
Iterator<?>it=al.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
*/
}
}
class Person
{
private String name;
Person(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
}
class Student_3 extends Person
{
Student_3(String name)
{
super(name);
}
}
/*
class Student implements Comparable <Person> //<? super E>
{
public int comparaTo(Person s)
{
this.getName();
}
}
class Comp implements Comparator<Student_3>
{
public int compare(Student_3 s1,Student_3 s2)
{
return s1.getName().compareTo(s2.getName());
}
}
TreeSet<Student>ts=new TreeSet<Student>(new Comp());
ts.add(new Student("abc1"));
ts.add(new Student("abc1"));
ts.add(new Student("abc1"));
ts.add(new Student("abc1"));
*/
泛型的形式
泛型类
:即自定义泛型类 泛型的形式
A
:当类中要操作的引用数据类型不确定时,早期定义
Object
来完成扩展,现在定义泛型来完成
B
:局限性:泛型类定义的泛型,在整个类中有效,如果该泛型类的方法被调用,
当泛型类的对象明确要操作的类型后,所有要操作的类型就被固定。
import java.util.*;
class Worker
{
}
//泛型前做法用Tool操作Worker
class Tool
{
private Object obj;
public void setObject(Object obj)
{
this.obj = obj;
}
public Object getObject()
{
return obj;
}
}
//泛型后做法<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">用Utils操作Worker</span>
/*
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
早期定义Object来完成扩展。
现在定义泛型来完成扩展。
*/
class Utils<QQ>
{
private QQ q;
public void setObject(QQ q)
{
this.q = q;
}
public QQ getObject()
{
return q;
}
}
class GenericDemo3
{
public static void main(String[] args)
{
//用Utils操作Worker
Utils<Worker> u = new Utils<Worker>();
u.setObject(new Worker());
Worker w = u.getObject();
/*
//用Tool操作Worker
Tool t = new Tool();
t.setObject(new Worker());
Worker w = (Worker)t.getObject();
Tool t = new Tool();
t.setObject(new Worker());
t.getObject();
*/
}
}
张老师之泛型加强版
代码如下:
package cn.itcast.day2;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.sql.Date;
import java.util.Set;
import java.util.Vector;
import cn.itcast.day1.ReflectPoint;
import com.sun.org.apache.bcel.internal.generic.Type;
//dao操作 -->crud 增删改查
//定义泛型类
public class GenericDao<T> {
/**
* @param args
*/
public void add(T t){
}
public void delete(T obj){
}
public void delete(int id){
}
public void update(T obj){
}
/*
//当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。
//因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
如果想用的话,就把泛型定义在方法上
public Static<T> void update2(T obj){
}
*/
//查集合
public Set<T> findByConditions(String where){
return null;
}
public T findByserName(String name){
return null;
}
public T findByTd(int id){
return null;
}
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>();
dao.add(new ReflectPoint(3,3));
/*
<span style="background-color: rgb(255, 255, 0);">//知识点 通过反射获得泛型的参数化类型</span>
Method applyMethod = GenericDao.class.getMethod("applyVector",Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
System.out.println(pType.getRawType());//得到原始类型//class java.util.Vector
System.out.println(pType.getActualTypeArguments()[0]);//得到实际参数类型//class java.util.Date
*/
}
}
泛型方法:
泛型放在返回值前面,修饰符的后面
A:
为了避免泛型类的局限性,让不同方法可以操作不同的类型,而且类型还不确定,
则可以将泛型定义在方法上
B:
特殊之处:静态方法不可以访问类上定义的泛型,
如果静态方法操作的应用数据类型不确定,可以将泛型定义在静态方法上
/*
//泛型类,将泛型定义在类上
class Demo<T>
{
public void show(T t)
{
System.out.println("Hello show!"+t);
}
public void print(T t)
{
System.out.println("Hello print!"+t);
}
}
*/
/*
泛型类定义的泛型,在整个类中有效。如果被方法使用,
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
为了让不同方法可以操作不同类型,而且类型还不确定。
那么可以将泛型定义在方法上。
*/
//泛型方法,将泛型定义在方法上
class Demo
{
public <T> void show(T t)
{
System.out.println("Hello show!"+t);
}
public <Q> void print(Q q)
{
System.out.println("Hello print!"+q);
}
/*
特殊之处:
静态方法不可以访问类上定义的泛型。
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
*/
public static <W> void method(W t)
{
System.out.println("method:"+t);
}
}
class GenericDemo4
{
public static void main(String[] args)
{
//泛型方法的
Demo d = new Demo();
d.show("hahah");
d.show(new Integer(4));
/*
//泛型类的
Demo<String> d = new Demo<String>();
d.show("haha");
d.print("xiix");
Demo<Integer> d = new Demo<Integer>();
d.show(new Integer(4));//同下,新特性
d.print(5);//同上新特性,
*/
}
}
张老师之加强版讲解
代码如下:
package cn.itcast.day2;
import java.sql.Date;
import java.util.*;
import java.awt.image.DataBuffer;
import java.lang.reflect.*;
import com.sun.org.apache.bcel.internal.generic.Type;
public class GenericTest {
public static void main(String[] args) throws Exception{
<span style="background-color: rgb(255, 255, 0);">//知识点 泛型集合类的综合案例</span>
HashMap<String,Integer> map = new HashMap<String,Integer>();
map.put("zxx", 28);
map.put("lhm", 35);
map.put("flx", 33);
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for(Map.Entry<String, Integer> entry : entrySet)
{
System.out.println(entry.getKey() + ":" + entry.getValue());
}
/*
Set<Map.Entry<Student,String>> entrySet = hm.entrySet();
Iterator<Map.Entry<Student,String>> ite = entrySet.iterator();
while(ite.hasNext())
{
Map.Entry<Student,String> me = ite.next();
Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu+"\\\\"+addr);
}*/
add(3,5);
Number x1 = add(3.5,3);
Object x2 = add(3,"abc");
swap(new String[]{"abc","xyz","itcast"},1,2);//只有引用类型才能作为泛型方法的实际参数,对于add方法,
//使用基本类型的数据进行测试没有问题,这是因为自动装箱和拆箱了。 swap(new int[3],3.5);语句会报告编译错误,
//这是因为编译器不会对new int[3]中的int自动拆箱和装箱了,
//因为new int[3]本身已经是对象了,你想要的有可能就是int数组呢?它装箱岂不弄巧成拙了
Object obj = "abc";
String x3 = autoConvert(obj);
copy1(new Vector<String>(),new String[10]);
copy2(new Date[10],new String[10]);
}
private static <T> void swap(T[] a,int i,int j)
{
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
//定义泛型方法
private static <T> T add(T x,T y)
{
return null;
}
//以下是泛型方法的练习
//定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中
public static <T> void copy1(Collection<T> dest,T[] str){
}
//定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。
public static <T> void copy2(T[] dest,T[] src){
}
//采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。
public static<T> void printCollection2(Collection<T> collection){
System.out.println(collection.size());
for(Object obj : collection)
{
System.out.println(obj);
}
}
//定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。
private static <T> void fillArray(T[] a,T obj)
{
for(int i=0;i<a.length;i++)
{
a[i] = obj;
}
}
//编写一个泛型方法,自动将Object类型的对象转换成其他类型。
private static <T> T autoConvert(Object obj)
{
return (T)obj;
}
}
两种泛型限定
限定通配符的上边界:向上限定:?extends E;
E
可以接收
E
类型或者
E
的子类
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
向上限定:?extends E;
E
可以接收
E
类型或者
E
的父类
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与
Vector<Object > x11 = new Vector<String>();
相似,
只能通过强制类型转换方式来赋值。
import java.util.*;
class GenericDemo6
{
/*
public static void main(String[] args)
{
ArrayList<String> al = new ArrayList<String>();
al.add("abc1");
al.add("abc2");
al.add("abc3");
ArrayList<Integer> al1 = new ArrayList<Integer>();
al1.add(4);
al1.add(5);
al1.add(6);
printColl(al);
printColl(al1);
}
//单独封装一个类,提高代码复用性
public static void printColl(ArrayList<?> al)//通配符
{
Iterator<?> it = al.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
}
*/
public static void main(String[] args)
{
ArrayList<Person> al = new ArrayList<Person>();
al.add(new Person("abc1"));
al.add(new Person("abc2"));
al.add(new Person("abc3"));
//printColl(al);
ArrayList<Student> al1 = new ArrayList<Student>();
al1.add(new Student("abc...1"));
al1.add(new Student("abc...2"));
al1.add(new Student("abc...3"));
printColl(al1);
}
public static void printColl(ArrayList<? extends Person> al1)//<? extends E>//泛型的限定,上限
{
Iterator<? extends Person> it = al1.iterator();
while (it.hasNext())
{
System.out.println(it.next().getName());
}
}
}
class Person
{
private String name;
Person(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public String toString()
{
return "person:"+name;
}
}
class Student extends Person
{
Student(String name)
{
super(name);
}
}
//泛型限定二
import java.util.*;
class GenericDemo7
{
public static void main(String[] args)
{
TreeSet<Student> ts = new TreeSet<Student>(new Comp());
ts.add(new Student("abc03"));
ts.add(new Student("abc02"));
ts.add(new Student("abc01"));
ts.add(new Student("abc06"));
Iterator<Student> it = ts.iterator();
while(it.hasNext());
{
System.out.println(it.next().getName());
}
TreeSet<Worker> ts1 = new TreeSet<Worker>(new Comp());
ts1.add(new Worker("abc..03"));
ts1.add(new Worker("abc..02"));
ts1.add(new Worker("abc..01"));
ts1.add(new Worker("abc..06"));
Iterator<Worker> it1 = ts1.iterator();
while(it1.hasNext());
{
System.out.println(it1.next().getName());
}
}
}
/*
class StuComp implements Comparator<Student>
{
public int compare(Student s1,Student s2)
{
return s1.getName().compareTo(s2.getName());
}
}
class WorComp implements Comparator<Worker>
{
public int compare(Worker w1,Worker w2)
{
return w1.getName().compareTo(w2.getName());
}
}
*/
class Comp implements Comparator<Person>//泛型的限定< ? super E> 下限
{
public int compare(Person p1,Person p2)
{
return p1.getName().compareTo(p1.getName());
}
}
class Person
{
private String name;
Person(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public String toString()
{
return "person:"+name;
}
}
class Student extends Person
{
Student(String name)
{
super(name);
}
}
class Worker extends Person
{
Worker(String name)
{
super(name);
}
}
总结: