java泛型

泛型的本质
   
    # 从数据结构角度看:
    泛型是一种效果,是不同的存储方式,执行的操纵是一样的效果。
    使用泛型,一旦定义了一个算法,就独立于任何特定的数据类型,而且不需要额外的操作,就可以将这个算法应用到各种
    数据类型中。许多 算法 不论运用哪一种数据类型,他们在逻辑上是一样的。
    泛型很强大,以至于从 根本上 改变了java代码的  编写方式。
  

    # 从java角度看:
    使用泛型,可以建立以 类型安全模式 处理 各种数据 的类、接口和方法。
    泛型是一种安全机制。
    举例说明:在集合中存入数据的时候,强行规定存入的数据类型,就可以避免安全问题。
 ####################################################################################### 
   java的泛型:::::::
   下面看看具体的泛型:
 
      # 泛型在本质上是指 类型参数化 。
     
      类型参数化:是指用来 声明 数据的类型的 本身,也是可以改变的,有实际的参数来决定。
                  一般情况下,实际参数决定了形式参数的 值 。而类型参数化,则是实际参数的类型决定的形式参数的 类型 。
                  举例说明:Integer max(Integer a,Integer b){
                             return a>b?a:b;
                             }   
                      如果比较的换成了double或float,不得不再次重新写代码。泛型就是一种不必确定参数的 类型 ,
                   等到调用的时候在确定参数的 类型 。
                      在泛型出现之前用Object声明 参数类型 变通,但是需要强制类型转换,有了泛型就方便多了。
                   所有的强制转化变为自动或者隐式的。
   
泛型的具体java应用 
  
   也可以说在 类型参数化的时候使用泛型。下面的应用都是可以使用类型参数化的时候使用泛型;
              特点:避免强转、安全性(强行规定集合的数据类型)、扩展性
   
《一》泛型类

例子:
      //声明一个泛型类
     public class Generic<T>{
          T ob;//ob的类型是T,需要到创建时候对象才能确定。
          Generic(T o){
            ob = o;
          }
      
          T getOb(){
            return ob;
          }
          void showType(){
             System.out.println("Type of T is:"+ob.getClass().getName() );      
          }
     }
     //下面使用上面的泛型
    public class demoGeneic{
      public static void main(String args[]){
      //声明一个Integer的Generic变量
      Generic<Integer> iobj;
      //创建一个Integer的Generic对象
      iobj = new Generic<Integer>(100);
      iobj.showType();
      int k = iobj.getOb();
      System.out.println("k = "+ k);
 
      //声明一个String的Generic变量
      Generic<String> sobj;
      //创建一个String的Generic对象
      iobj = new Generic<String>("hello");
      sobj.showType();
      String s = sobj.getOb();
      System.out.println("s = "+ s);  
      }
    }
    结果:Type of T is:java.lang.Integer
          k = 100
          Type of T is:java.lang.String
          s = hello    
注意:类型参数T不能使用在静态方法中,并且不能出现<int>。


《二》泛型方法

创建一个泛型方法常用形式:

    [访问权限] [static][final]<类型参数列表> 返回值类型 方法名([形式参数列表])
 
             必须这样写,  泛型方法可以写在一个泛型类中,也可以写在普通方法中,由于在泛型类中
             的任何方法,本质上都是泛型方法,所以在实际使用中,很少会在泛型类中
             再用上面的形式来定义泛型方法。
               类型参数可以用在方法体中修饰局部变量,也可以用在方法的参数表中,修
             饰形参。
               泛型方法可以是实例方法或者静态方法。类型参数可以使用在静态方法中,
            这是与泛型类的重要区别。

使用一个泛型方法:  <对象名|类名>.<实际类型>方法名(实际参数类型) ;
                 和
                    <对象名|类名>.方法名(实际参数表);
                如果泛型方法是实例方法。要使用对象名作为前缀,如果是静态方法,则可以
             使用对象名或类名作为前缀。    
                如果是在类的内部调用,且采用的是第二种,那么前缀都可以省略。
看看例子吧:
 public class demoGenMethods{
   //定义泛型方法,有一个形式参数用类型参数T来定义
   public static <T> void showGenMsg(T ob,int n){
        T localOb = ob;//局部变量也可以用类型参数T来定义
           System.out.println(localOb.getClass().getClass().getName());
   }
   public static <T> void showGenMsg(T ob){
     System.out.println(ob.getClass().getName());
   }
  public static void main(String args[]){
     String str = "parameter";
     Integer k = new Integer(123);
    
     //用两种方法调用泛型方法
     demoGenMethods.<Integer>showGenMsg(k,1);
     showGenMsg(str); 
     showGenMsg(k);//这就是泛型方法的好处,同一个方法接受的参数类型在同一个类中可以不同。
  } 
 }
注意:静态方法不可以访问类中的泛型。泛型也是一个对象,从某种意义上说。
      因为泛型在创建对象的时候才执行,而静态方法在加载的时候,对象不存在。
    
      如果静态方法操作的数据类型不确定,可以将泛型定义在方法上。注意泛型方法的格式。

《三》泛型接口
        

 interface 接口名<类型参数表>
    看例子:
         创建接口:interface MinMax<T extends Comparable<T>>
         实现接口:class MyClass<T extends Comparable<T> > implements MinMax<T>
 
    看不同:     MinMax<T extends Comparable<T>>和MinMax<T> 如果写法还是一样就不能通过编译。
          至于class MyClass<T extends Comparable<T> >也要写成泛型,是因为:一个类实现了一
          个泛型接口则此类也是泛型类。但是如果父类的是这种 implements MinMax<T> 形式就不必
          在这样class MyClass<T extends Comparable<T> >写啦。

《四》泛型类的继承
           
            泛型类的子类必须将泛型父类所需要的类型参数,沿着继承链向上传递。这与
            构造方法 参数 必须沿着继承链向上传递的方式类似。

  1、以 泛型类 为父类
     public class derivedGen <T> extends superGen <T>
    一定要这样写,意味着传递给derivedGen的实际类型也会传递给superGen。
    看一个例子:public class derivedGen<T ,U> extends superGen<T>{
                 U  dob;
                 public derivedGen(T ob1,U ob2){
                        super(ob1);//传递参数给父类!!!!!!!!!!!
                        dob = ob2;//为自己的成员赋值
                 }
                 public U getDob(){
                      return dob;
                 }
                }  
  2、以 非泛型类 为父类
        此时,不需要传递类型参数给父类,所有的类型参数都是为自己准备的。


技巧


一、上界使用:
     class Stats<T extends Comparable & Serializable>

  例子:求数组中元素的平均值(有int double )
 
     class Stats<T>{
       T [] nums;
       Stats (T [] obj){
          nums = obj; 
       }
       double average(){
          double sum = 0.0;
          for(int i = 0;i<nums.length;i++)
              sum += nums[i].doubleValue();//这里有错误
          return sum/nums.length;  
       }
     } 
   分析: nums[i].doubleValue()所有的Number都有这个方法,为甚系统还找不到这个方法?
          原因就是,使用了泛型,没有告诉系统说参数一定要是Integrt 或 Double,要是String
          也可以。解决这个问题就要用到下面的 有界类型。
在例子:class Stats<T extends Number>{
       T [] nums;
       Stats (T [] obj){
          nums = obj; 
       }
       double average(){
          double sum = 0.0;
          for(int i = 0;i<nums.length;i++)
              sum += nums[i].doubleValue();//这里正确!
          return sum/nums.length;  
       }
     } 

还有<? super E>可以传父类的对象,局限性就是这个对象只能用父类的方法。

二、通配符参数
   看下面的例子就又会出现问题:
   class Stats<T>{
     .....
      void doSomething(Stats <T> ob){
           Systemout.println(ob.getClass().getName());
      }
   }  
   如果在使用时:
     Integer inums[] = {1,2,3,4,5};
     Stats <Integer> iobj = new Stats<Integer>(inums);
     Double dnums[] = {1.1,2.2,3.3};
     Stas <Double> dobj = new Stats<Double>(dnums);
     dobj.doSomething(iobj);//错误
 分析:  void doSomething(Stats <T> ob)是这样声明的所以iobj和dobj的类型要一致。
      要解决必须使用通配符啦!再看改过的例子:
class Stats<T>{
     .....
      void doSomething(Stats <?> ob){
           Systemout.println(ob.getClass().getName());
      }
   }  
   如果在使用时:
     Integer inums[] = {1,2,3,4,5};
     Stats <Integer> iobj = new Stats<Integer>(inums);
     Double dnums[] = {1.1,2.2,3.3};
     Stas <Double> dobj = new Stats<Double>(dnums);
     dobj.doSomething(iobj);//正确
通配符的使用:Stats <? extends Integer> ob
也可以将方法定义为泛型方法。区别:用泛型方法的情况可以用T接受;
***************************************************
运行时类型识别:

 可以采用反射机制和传统的方法。
     需要注意的,在JVM中,泛型类的对象总是一个特定的类型,此时,他不再是泛型。所以,
  所有的类型查询都只会产生原始类型。
   a instanceof Generic <?> 或者   a instanceof Generic <>   "?"被忽略
  但是不能写为 a instanceof Generic <Integer>       


擦拭:程序员不必知道有关java编译器将源代码转化为class文件的细节。但是知道更好呵呵!

 工作原理:当Java代码被编译时,全部泛型类型的信息会被删除,也就是使用类型参数来代替他们的
           界限限制,默认的是Object ,然后运用相应的强制转化以维持与类型兼容,编译器会强制
           这种类型兼容。对于泛型来说这种方法意味着在运行时不存在类型参数,他们仅仅只是
           一种源代码机制。

擦拭会带来错误:1、静态成员共享问题
                   在泛型类中可以有静态的属性或者方法。静态方法不能使用参数类型。那么,其中
                   的静态成员也不可以使用参数类型或者是本泛型类的对象。
                2、重载冲突问题
                   void conflict(T o){}
                   void conflict(Object o){}
                  在编译时,T会被Object所取代,所以实际上他们两个是一样的,就会出现错误。
                  另一种:public int conflict(foo<Integer>){}
                          public int conflict(foo<String>){}
                  编译器会怀疑他们有冲突,还可以更正:
                          public int conflict(foo<Integer>i){}
                          public int conflict(foo<String>s){}
               3、接口实现问题
                   由于接口也可以是泛型接口,而一个类又可以实现多个泛型接口,所以会引发
                   冲突。例如:
                   class foo implements Comparable<Integer>,Comparable<Long>
                   由于会擦拭所以这两个接口还以一样的在编译期间。 要实现泛型接口
                   只能实现具有不同擦拭效果的接口,否则用:
                      class foo<T> implements Comparable<T>
 
     
泛型的局限:
           1、不能使用基本类型
           2、不能使用泛型类 异常
           3、不能使用泛型数组
           4、不能实例化参数类型对象      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值