A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 冯飞 初级黑马   /  2013-2-20 18:30  /  1349 人查看  /  7 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 ie_feng 于 2013-2-21 19:03 编辑

对泛型有诸多不理解,有哪位大神给详细解释一下泛型的思想。

7 个回复

正序浏览
王钊 中级黑马 2013-2-20 22:53:41
7#
这个主要还得你自己多看多理解,不过我有一句话应该能让你对泛型有所感悟。
所谓泛型:就是变量类型的参数化。
回复 使用道具 举报
百度一下 你就知道{:soso_e113:}
回复 使用道具 举报
这是我看泛型后写的博客
地址:http://blog.csdn.net/zhangjinyu1991/article/details/8575002
回复 使用道具 举报
引入泛型
[html] view plaincopy
Integer max(Integer a, Integer b);  //整形数据比较  
String max(String a, String b); // 字符型数据比较  
/***更加简洁的实现***/  
T max(T a, T b);    // 比较大小返回更大的那个对象  



我们知道有许多的处理过程或者处理模式与具体的数据关系不大。像上面对于整形数据和字符型数据大小的比较如果我们使用重载的方式来实现不同数据类型的max(),这种含有大量雷同代码的设计方式会让软件工程师们设计和维护起来比较麻烦。出于这个问题,我们使用泛型来简单的解决它。

注意:也许你会想:用Object来刻画数据不就行了?

行吗,转换一下就是 Object max(Object a, Object b); 这样可以填充数据了,但是我们要求a,b所属的类型要有可比性,假如a是Integer型b是String型也是可以填充的,但他们没有可比性将会导致运行时错误。

那为什么泛型就可以呢?来讲讲泛型的功能。

泛型的功能
泛型:是一种能表达能够表达更广泛数据的类型,注意咯,泛型变量的值是一种类型,而非类型取值范围内的值。

泛型机制是一种特殊的类型机制,有两方面的功能:

1. 通用型功能:泛型机制提供一种特殊的带参数描述方式(特殊在于:以类型为参数)  比如上面:T是形式参数,T可以取值Integer、String这些类型。

2. 类型检查功能:说到泛型的类型检查,这就是为什么上面那个例子可以用泛型而不能用Object的原因了。借助编译器当你传入的数据a为整形数据而b为字符串数据时编译阶段会报错。

注意:泛型的检查是在编译阶段的,对于这点要谨记。在java的泛型机制中,必须在编译阶段确知填入的具体类型,不支持在程序的运行阶段传入类型参数。普通的参数都是在运行时传入的,但泛型参数必须在编译时确定。

泛型的申明
class 类名 <类型参数列表>{类体}

如: class A<T1, T2> {T1 a; T2 b;}  // 在类体中,你把T1,T2当做已知类来使用就好了

泛型的具体化
类名 <具体类型列表> 变量=new 类名<具体类型列表>{构造函数的参数列表}  // 注意:具体类型必须是引用型类型

如:A<Integer, String> x=new A<Integer, String>();

泛型参数可以出现在什么地方
泛型参数可以出现在类、接口、成员方法、内嵌类、内嵌接口中

值得注意的是:泛型方法不仅可以出现在泛型类中,也可以出现在普通类中。不过,当泛型方法出现在普通类中时,记得要在泛型方法中申明泛型参数。 如:

<T> void function(T a){……}

泛型约束和泛型通配符
泛型约束:<T extends BoundingType>  // BoundingType 可以是类,也可以是接口;可以有多个BoundingType,用&隔开;但只能有一个类并必须放在第一位置

表示 T必须是BoundingType的子类型(具体化时确定了),或者是实现了BoundingType这个接口的类型

如:<T extends Number> // 要求T的值是Number的子类,且不能是Number类型

       <T extends Comparable>  // 要求T的值(类型)是个实现了Comparable接口的类

泛型中的通配符:<? extends T>  // T即可以是类也可以是接口,把?理解为类型参数,T是类型的上界;?必须是T的子类

来比较一下:  LinkList<Number>  // 以具体化,说明LinkList类中的数据必须是Number类型或者Number的子类型

                       LinList<T extends Number>   // T标识的是一种确定的类型参数,需要要具体化的

                       LinkList<? extends Number>  // ?标识的是一种不确定的类型参数,不要具体化的

来验证一下LinkList<Number>的LinkList类中可以是Number的子类型:

[java] view plaincopy
public class Test {  
  
    /**
     * @param args
     */  
    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        TestClass<Number> test=new TestClass<Number>(3);  
        TestClass<Number> test1=new TestClass<Number>(2.33);  
        //TestClass<Number> test=new TestClass<Integer>(3); 注意:编译报错  
        System.out.println(test.getData());  
        System.out.println(test1.getData());  
    }  
  
}  
class TestClass<T>{  
    private T date;  
    TestClass(T data){  
        this.date=data;  
    }  
    public void  setData(T data){  
        this.date=data;  
    }  
    public T getData(){  
        return this.date;  
    }  
}  
输出:  
3  
2.33  

这样看起来感觉后面两个差不多,其实是非常有区别的,举个列子老看下:

[java] view plaincopy
// 一个链表,节点中的数据必须是可比较的  
  
import java.util.Scanner;  
  
public class Ch_11_3 {  
  
    /**
     * @param args
     */  
    public static void main(String[] args) {  
        // 整形数据链表  
        System.out.println("创建整数链表,以-1结尾:");  
        Scanner in=new Scanner(System.in);  
        LinkList<Integer> int_list=new LinkList<Integer>();  
        int x=in.nextInt();  
        while(x!=-1){  
            int_list.addNode(x);  
            x=in.nextInt();  
        }  
        int_list.printList();  
        System.out.println();  
         
        // String类型数据链表  
        System.out.println("输入一组String,以空格为分隔符:");  
        LinkList<String> string_list=new LinkList<String>();  
        Scanner in_2=new Scanner(System.in);  
        String str=in_2.nextLine();  
        String[] str_array=str.split(" ");  
        for(String s : str_array){  
            string_list.addNode(s);  
        }  
        string_list.printList();  
         
    }  
  
}  
class LinkList<T extends Comparable>{//Comparable 是个接口 <T extends BoundingType> Bounding可以是个类也可以是个接口  
    // 定义节点类  
    class Node{  
        private T data; //节点存储的数据  
        private Node next; //指向后一节点的指针  
         
        public Node(T x){   // 构造函数  
            this.data=x;      
        }  
        public Node getNext(){  //获取节点的后一个节点  
            return this.next;  
        }  
        public void setNext(Node p){    //设置指针的指向元素  
            this.next=p;  
        }  
        public T getData(){ //获取节点元素  
            return this.data;  
        }  
        public void setData(T d){   //设置节点元素  
            this.data=d;  
        }  
    }  
      
    private Node head,tail; // 头指针和尾指针  
    public LinkList(){  
        head=tail=null;  
    }  
    public boolean isEmpty(){   //判断是否为空链表  
        return head==null;  
    }  
    public Node getHead(){  //获取表头  
        return head;  
    }  
    private T max(T a, T b){    //比较两个节点的元素大小  
        return (a.compareTo(b)>0)?a:b;  
    }  
    public T getMaxValue(){ //获取链表中的最大值  
        if(isEmpty()){  
            return null;  
        }  
        T max_value=this.head.getData();  
        Node p=this.head.getNext();  
        while(p!=null){  
            max_value=max(p.getData(),max_value);  
            p=p.next;  
        }  
        return max_value;  
    }  
    public void printList(){  
        Node p=head;  
        while(p!=null){  
            System.out.print(p.getData()+"==");  
            p=p.getNext();  
        }  
    }  
    public void addNode(T x){  
        Node p=new Node(x);  
        if(isEmpty()){  
            head=tail=p;  
        }  
        else{  
            tail.setNext(p);  
            tail=p;  
        }  
    }  
      
}  

假如我现在要求添加一个sum()方法:用于计算节点中数据的和应该怎么办?
首先,要求节点数据要可以累加,说明节点数据是数值型,那么如何实现呢?

[java] view plaincopy
/*
    public double sum(LinkList<Number> ls){
        return 0;
    }//Bound mismatch: The type Number is not a valid substitute for the bounded parameter <T extends Comparable> of the type LinkList<T>
     // 意思说Number不是一个可比较的类型
    */  
    public double sum(LinkList<? extends Number> ls){  
        return 0;     
    }// 编译通过  

泛型中的通配符是泛型部分最难的,我也没有完全理解关于通配符的细节可以参考IBM developerworks中的一篇文章:http://www.ibm.com/developerworks/cn/java/j-jtp04298.html
泛型机制原理(很重要)
我总结了几点:

1, JVM不支持泛型

2,    泛型实现机理:擦拭(所谓擦拭,就是编译器对泛型类/接口在编译前会对其实施转换,将其转换成普通类/接口)

对于泛型擦拭机理的学习我建议通过反编译字节码文件的方式来学习(反编译得到了的源码是擦拭后的,没有泛型):~$   javap  类名

意错点
1,  T  t=new  T();   // 编译错误,不能创建泛型对象

2,  T<String>  t1=new  T<String>();

     T<Integer> t2=new  T<Integer>();

     t1=t2;
     // 编译错

3,  class  A  extends  <T>  {}

      class  B  implements  <T>  {}

     //  编译错误  , 因为将会破坏java单一继承原则

4,    重载方法
     class  A<T1, T2>{

            public    void   function(T1 t1){;}
            public    void   function(T2 t2){;}
     }

     // 编译错误,假如T1,T2都是String,拿着两个函数就完全相同了

评分

参与人数 1黑马币 +10 收起 理由
冯飞 + 10 很给力!

查看全部评分

回复 使用道具 举报
看看这篇关于泛型的总结也许对你有所帮助:http://www.cnblogs.com/Occasionally-desert/archive/2013/02/11/2910105.html
回复 使用道具 举报
在没有泛型前,一旦把一个对象丢进java集合里,集合就会忘记对象的类型,把所有的对象都当成Obeject类型来处理.当从集合里取出对象后,就需要进行强制类型转换,因为你需要的类型和你取出的类型是不一致的.这种强制类型转换使代码臃肿,而且容易引起ClassCastException(类型转换异常).
自从1.5增加了对泛型的支持,就可以记住集合中元素的类型,并可以在编译时检查集合中元素的类型,如果试图向集合中添加不满足指定类型要求的对象,编译器就会提示错误.增加泛型后代码更简洁,程序更健壮.
另外程序编写的原则也要求程序员尽量将问题在编译时处理,从此意义上讲泛型也很有用.
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马