黑马程序员 java基础之泛型
2013-05-14 21:04
260 查看
/* 泛型: JDK 1.5之前,对象保存到集合中就会失去其特性,取出时要手动进行人工强制类型转化, 集合元素中的数据类型可以不统一 例:List集合 list=new ArrayList(); list.add("aa"); list.add(1); list.add(12.5); 加入泛型之后,list集合只能放同一类型的数据,就避免了list元素类型不统一的现象出现。 JDK1.5版本以后出现新特性,用于解决安全问题,是一个类型安全机制. 类比:数组 int[]arr=new int[3]; arr[0]=1; arr[1]=3.5//编译不能通过,报损失精度 数组在定义时已指定要存入的类型 优点: .将运行时期出现问题ClassCastException,转移到了编译时期 有利于程序员发现问题,解决问题->使运行时期问题减少->减少安全隐患 .避免了强制转换的麻烦 */ package generic; import java.util.*; class GenericDemo{ public static void main(String[] args){ //定义了一个as容器,强制限定(<String>)只能存放String类型的对象 ArrayList<String> as= new ArrayList<String>(); //ArrayList as =new ArrayList(); as.add("abc"); as.add("bef"); //as.add(4);//Integer.valueOf(4) /* 如果向集合中添加整形对象,和字符串 属于不同类型的对象,在运行时,下面的强转会发生异常 ClassCastException 存在安全隐患,能不能再编译时期发生问题? ->引出泛型 */ //集合中的元素通过迭代器取,迭代器可以操作(Object)任意对象 //因此,明确迭代器操作的类型,更方便操作 //其实在上面已指明String,iterator方法返回就是Iterator<String> //需要一个相同类型进行接收. for(Iterator<String>it = as.iterator();it.hasNext(); ){ //String str= (String)(it.next()); String str = it.next(); System.out.println(str+" "+str.length()); } } } /* 泛型格式:通过<>来定义要操作的引用数据类型 在使用java提供的对象时,是么时候写泛型呢? 通常在集合框架中和常见,只要见到<>就要定义泛型 例如: Collection<E>:E->Element 当使用集合时,将集合中要存储的数据类型(引用类型)作为参数传递到<>(如同函数传参)中即可 ArrayList<String> as=new ArrayList<String>(); */ /* 泛型与原数据: ArrayList as= new ArrayList<String>(); as.add(1); 会报警告,但可以存入 as.add(1)->说明此时E的类型没有传入 ArrayList<String>as= new ArrayList(); 会发出警告,如果加上 as.add(1)时编译报错->此时E的类型已为String */
将Comparator的例子利用泛型改写
package generic; import java.util.*; class MyComp implements Comparator<String>{//Comparator<T>:T->Type 数据类型 public int compare(String obj_1,String obj_2){ //int num=obj_1.length()-obj_2.length(); int num=obj_2.length()-obj_1.length(); System.out.println(obj_1+"..."+obj_2+" "+num); /* 比较: a,a 0 bc,a a-bc<0 def a a-def<0 def bc bc-def<0 */ /* 可通过这样方式,逆序输出(长度从大到小) 也可用更简便的方式如上. if(num>0) return -1; if(num<0) return 1; */ if(num==0) return obj_2.compareTo(obj_1); return num; } } class GenericDemo2{ public static void main(String[] args){ TreeSet<String> ts = new TreeSet<String>(new MyComp()); ts.add("a"); ts.add("bc"); ts.add("def"); ts.add("e"); for(Iterator<String> it = ts.iterator();it.hasNext();){ System.out.println(it.next()); } } } /* 注意在使用HashSet集合中, 存放的对象所属的类复写hashCode和equals 注意equals原型为Object中public boolean equals(Object obj)//Object不能变 */ /* 目前为止:自定义一个类 ①需要复写Object中的int hashCode()和equals 因为:可能存入HashSet集合 ②需要实现Comparable复写compareTo 因为:需要使该类对象具备比较性->存入TreeSet集合 用不用,另说. */
自定义泛型类
/* 在自定义的类中使用泛型 什么时候定义泛型类? 当类中要操作的引用数据类型(不能是基本数据类型)不确定的时候 早期定义Object来完成扩展. 现在定义泛型来完成扩展 */ package generic; class Worker{ } class Student{ } //没有泛型前的做法 class Tool{ private Object obj; public void setObject(Object obj){ this.obj=obj; } public Object getObject(){ return obj; } /* 之所以定义为Object,接收后期的任意对象(后期传入的不确定) */ } //泛型类 class Utils<Q>{//在后面会说到通配符?在这里不能换成<?> //编译会报需要<标示符>,如果这里用?->传过来的类型也无法在类中使用 private Q q; public void setObject(Q q){ this.q=q; } public Q getObject(){ return q; } } class GenericDemo3{ public static void main(String[] args){ /* //未使用泛型 Tool t = new Tool(); t.setObject(new Worker()); //t.setObject(new Student());当传入学生对象,编译时期没错,但是运行时期发生转换异常 Woker w = t.getObject(); */ //使用泛型 Utils<Worker> u = new Utils<Worker>(); u.setObject(new Worker()); Worker w = u.getObject(); } }
泛型方法
/* 泛型方法引入: 泛型类定义的泛型,在整个类中有效.如果被方法使用 那么泛型类的对象明确要操作的具体类型后,所有要操作的类型 就已经固定了. 为了让不同方法可以操作不同的类型,而且类型还不确定 那么可以将泛型定义在方法上. 格式: 修饰符 <T,S...> 返回值类型 方法名(形参表){ } */ /* 特殊之处: 静态方法不可以访问类上定义的泛型. 这是因为泛型类上的<T>只有在建立对象时,才能明确其引用类型,静态方法(随着类加载而加载)存在时,可能还没有对象 也就是说此时T不能被明确. 如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上. */ package generic; /* class Test<T>{//<T>对整个类有效,也就是说,一旦传入实参,只可能有一种引用类型 public void show(T t){ System.out.println("show "+t); } public void print(T t){ System.out.println("print "+t); } } */ //1.泛型方法 class Demo{ public <T> void show(T t){ System.out.println("show "+t); } //注意<T>与上面的<T>互不影响,因为只在该方法体中有效 //T类型 根据传入的实参的类型由编译器确定 public <T> void print(T t){ System.out.println("print "+t); } } //2.泛型类中包含泛型方法(对以上略作修改) class Demo2<T>{ public void show(T t){ System.out.println("show "+t); } public <Q> void print(Q t){ System.out.println("print "+t); } //3.泛型静态方法 public static <S> void printStatic(S t){ //注意泛型(<S>)定义在方法上 //放在返回值类型前面,修饰符后面 System.out.println("print "+t); } } class GenericDemo4{ public static void main(String[] args){ /* Test<String> t=new Test<String>();//限定为String类型 t.show("12"); t.print(new Integer(13));//错误 */ Demo d=new Demo(); d.show("haha"); d.show(new Integer(12)); d.print(new Double(1.3)); System.out.println(); Demo2<String> d2=new Demo2<String>(); d2.show("123");//show只能操作String类型->因为其随着泛型类的类型确定而确定 d2.print("haha");//而print可以传入任意引用类型->随着泛型方法的类型确定而确定 d2.print(new Integer(12)); System.out.println(); Demo2.printStatic("static"); } }
泛型定义在接口上
/* 泛型定义在接口上 */ package generic; interface Inter<T>{ public abstract void show(T t); } /* class InterImp1 implements Inter<String>{//使用接口时,往里面传参数String public void show(String q){ System.out.println("show "+q); } } */ //第二种方式:在实现时,操作的类型依然不确定 // 由调用者传入类型 class InterImp2<Q> implements Inter<Q>{ public void show(Q t){//复写父接口方法 System.out.println("show "+t); } } class GenericDemo5{ public static void main(String[] args){ new InterImp1().show("abcd"); new InterImp2<Integer>().show(12);//Integer-传给>InterImp2<Integer> //-传给>Inter<Integer> } }
?通配符/占位符
package generic; import java.util.*; class GenericDemo6{ public static void main(String[] args){ ArrayList<String> as = new ArrayList<String>(); as.add("abc"); as.add("def"); ArrayList<Integer> al = new ArrayList<Integer>(); al.add(10); al.add(12); print(al); print(as); } //public static void print(ArrayList<String> as){//只能接收String类引用 //而不能ArrayList<String> as= new ArrayList<Integer>(); //此时为了提高复用性->使用通配符(占位符)? public static void print(ArrayList<?> as){ //也可使用ArrayList as,因为:jdk1.4还没有泛型,老版本为了兼容新版本 //但是ArrayList as不严谨 for(Iterator<?> it=as.iterator();it.hasNext(); ) System.out.println(it.next()); //System.out.println(it.next().length());//如果传入Integer,则运行失败,Integer中无Length()方法 } /* 如果以上使用了形参: public static<T> void print(ArrayList<T> as) for(Iterator<T> it=as.iterator();it.hasNext(); ){ T t=it.next();//如果是?没法使用 T t; System.out.println(t); } */ }
泛型上限与下限:
上限
/* ? 通配符,或理解为占位符 泛型的限定: ? extends E: 可以接收E类型或者E的子类型->上限(限定的E(父类型),子类任意扩展) ? super E:可以接收E类型或者E的父类型->下限(限定的E(子类型),父类型任意) */ package generic; import java.util.*; class Animal{ private String name; Animal(String name){ this.name=name; } public String getName(){ return name; } } class Pig extends Animal{ Pig(String name){ super(name); } public void Method(){ System.out.println("method"); } } class Test{ } class GenericDemo7{ public static void main(String[] args){ ArrayList<Animal> as =new ArrayList<Animal>(); as.add(new Animal("a1")); as.add(new Animal("a2")); //as.add(new Pig("pig1"));//向上提升,可以存入 print(as); ArrayList<Pig>at = new ArrayList<Pig>(); at.add(new Pig("a1")); at.add(new Pig("a2")); //①print(at);不行//ArrayList<Animal> as=new ArrayList<Pig>(); /* ①ArrayList<Animal> as:我指定要接收一个能存储Animal引用类型的 集合,也就是说所既可以存储Animal对象,也可以存储其子类对象 ②new ArrayList<Pig>():传入了一个只能存储Pig引用类型的集合实体 只能存储Pig对象,如果Animal再有一个子类Bird,其对象将不能存储在该集合 实体中. 当然ArrayList<Pig> as=new ArrayList<Animal>();也不行 指定只能存Pig对象,而传入的集合可以存Animal及其子类对象 也就是说左右两边类型保持一致 */ print(at);//② } /* ① public static void print(ArrayList<Animal> as){ for(Iterator<Animal> it=as.iterator();it.hasNext(); ) System.out.println(it.next().getName()); } } */ //② 修改后 public static void print(ArrayList<? extends Animal> as){ //此时我想让其既打印Animal,又打印 //Pig,可以采用ArrayList<?> as //但是这样写:可以接收任意引用类型, //如果我只想让其接收Animal及其Animal子类型呢?? //此时必须做泛型限定即:ArrayList<? extends Animal> as //当传入new ArrayList<Pig>(),相当于ArrayList<Pig> as=new ArrayList<Pig>(); for(Iterator<? extends Animal> it=as.iterator();it.hasNext(); ) System.out.println(it.next().getName()); } }
下限
package test; import java.util.*; class Animal{ private String name; Animal(String name){ this.name=name; } public String getName(){ return name; } } class Pig extends Animal{ Pig(String name){ super(name); } } class Bird extends Animal{ Bird(String name){ super(name); } } class Demo{ public static void main(String[] args){ TreeSet<Pig>ts1 = new TreeSet<Pig>(new Com()); ts1.add(new Pig("P1")); ts1.add(new Pig("P2")); TreeSet<Bird>ts2 = new TreeSet<Bird>(new Com()); ts2.add(new Bird("B1")); ts2.add(new Bird("B2")); method(ts1); method(ts2); /* 构造函数: TreeSet(Comparator<? super E> comparator) 例如:E为Pig TreeSet(Comparator<? super Pig> comparator) 传入的比较器限定:比较的Pig对象或Animal对象或Object对象 也就是说对于以上其可传入类型为:Comparator<Pig>或Comparator<Animal>或Comparator<Object> */ } public static <T extends Animal> void method(TreeSet<T> ts ){ for(Iterator<T>it=ts.iterator();it.hasNext();){ System.out.println(it.next().getName()); } } } class Com implements Comparator<Animal>{ public int compare(Animal a1,Animal a2){ return a1.getName().compareTo(a2.getName()); } }
相关文章推荐
- 黑马程序员java学习<基础加强>—泛型
- 【黑马程序员】Java基础加强16:JDK1.5泛型
- 黑马程序员_Java基础_泛型应用 和 基本数据类型对象包装类
- 黑马程序员-java基础 泛型
- 黑马程序员_Java基础泛型
- 黑马程序员——JAVA基础---使用泛型
- 黑马程序员-Java基础加强之泛型
- Java基础---泛型、集合框架工具类:collections和Arrays (黑马程序员)
- 黑马程序员-Java基础知识预备之Java泛型
- 黑马程序员——Java基础——泛型
- 黑马程序员------Java基础学习------泛型
- 黑马程序员---java基础加强---jdk1.5新特性之泛型
- 黑马程序员_毕向东JAVA基础_集合(2)Collection&&List&&Set&&泛型
- 黑马程序员——Java基础---泛型、集合框架工具类:collections和Arrays
- 黑马程序员--Java基础加强--12.利用反射操作泛型I【与反射+泛型相关的接口类型综述】【Type】【ParameterizedType】【个人总结】
- 黑马程序员--Java基础加强--14.利用反射操作泛型III【解析关于泛型类型的细节信息的获取方法】【Method与泛型相关的方法】【个人总结】
- 黑马程序员——JAVA基础---泛型
- 黑马程序员——Java基础:集合类、泛型
- 黑马程序员——java基础——泛型
- 黑马程序员—15—java基础:有关泛型的学习笔记和学习心得体会