您的位置:首页 > 职场人生

黑马程序员 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());
}
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: