Java泛型总结

Java泛型 总结

Java泛型是 jdk5.0 以后的版本才有的特性,也是 Sun 公司将 JDK1.5. 更名为 JDK5.0 的一个重要原因,因为历时 5 年的开发,是 JDK1.5 的功能更为健全和完善。

重用度高,安全系数高是Java 的追求的目标,而泛型的运用正是这一个内容的重要体现。在学习 Java 的过程当中 Java 泛型感觉有些晦涩难懂。起先以为自己的泛型学的还不错,可是在很长一段时间研究 Java 泛型毫无进展的情况下,开始有些崩溃了,于是硬着头皮看啊看,终于觉得有点感觉了,所以,今天在这里总结一些 Java 泛型的一些知识,希望以后自己以后开发生涯中能够用上今天的经验,写下来的原因确实很多,我不想在遇到同样的问题而束手无策,而且记忆了有时候并不是十分的可靠,所以我准备了 4 天的时间来写这篇总结。希望能详尽的表述出来,这个非常适用的机制,并方便以后读到这篇文章的读者,能够提出你们对泛型的一些看法,而不仅仅是一个“顶”字,我也相信你们有比这更好的学习泛型的经验和教训。

什么是泛型? 泛型实质上是叫做泛型程序设计。运用泛型意味着编写的代码可以被很多类型不同的对象所 重用

为什么使用泛型?泛型是一种全新的程序设计手段,使用泛型的机制编写的程序代码,要比那些杂乱的使用Object 变量然后进行强制类型转化的代码明显的具有以下特点: 1 更好的安全性  更好的可读性。

了解这个两个问题才能从根本上了解泛型的好处,也才能真正的了解甚至熟练的运用Java 泛型。

先前受到c++ 模板的影响,所以一时还难以转过弯来。后来发现 C++ 的模板比 Java 的泛型做得好一些。一般情况下, Java 的特点是“懒加载”(源自 Hibernate 中的 lazy 这里只是为了说明),即运行时才动态的调用方法和生成新的类(除了 static 块和域)。所以 Java 并不知道你所指的泛型到底是什么类型。 Java 是一门纯面向对象的语言,所有的东西必须用类来表示(除了基本的数据类型外),所以类型可以泛化但是不能不让编译器始终不知道你所要泛化的东西是何种类型。因此有一定 C++ 基础的人千万要将模板和 Java 的泛型区分开来。

泛型最初的目的是希望类或者方法能够具备更加广泛的表达能力。了解泛型最主要的是要了解泛型的边界,以及它可以用来做什么,不能够做什么。我想这点是最重要的。

在此之前,我们用的最多的就是多态。所以经常性的让类来持有Object 类型的对象。

package  cn.ccsu.cooole.generics;

/**

  *   @author   CoolPrince

  *    这里讲的是一个类持有某一个类的对象本案例中用的是代理的机制

  *    即Holder_1类持有AObject的对象ao;

  *    这个通常情况下只能持有单个对象的类

  */

public   class  Holder_1 {

     private  AObject  ao  =  null ;

public  Holder_1(AObject ao) {

this . ao  = ao;

}

AObject  get(){ return   ao ; }

}

class  AObject{}

在举个例子(解释详见注释部分):

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *   描述:此案例讲的是以前我们所用到过的动态绑定,此时只用了一个Holder_2的对象

  * 就实现了存储两个不同的类型的对象的功能。

  *   有些情况下我们主要运用泛型是将其运用到容器当中,告诉指定容器要持有什么样类型     * 的对象   由编译器来保证类型的正确性,举个简单的例子   List <Integer>   list   =  

* new   ArrayList <Integer> ();   此时前后的类型都为Integer才能保证数据的正确性.

  */

public   class  Holder_2 {

     private  Object  a   =  null ;

public  Holder_2(Object a ) {   this . a  = a; }

     public   void  set(Object a){ this . a  = a; }

     public   Object  get(){ return   a ; }

public   static   void  main(String[] args) {

Holder h =  new  Holder();

Holder_2 h2 =  new  Holder_2(h);

print ( "Test Holder result is :" + h2.get());

String str =  new   String( "Name is /" String/"" );

h2.set(str);

print ( "Test String result is :" + h2.get());

}

private   static   void  print(Object o){

System. out .println(o);

}

}

class  Holder{

public  String toString(){ return   "Name is /"Holder/"" ;}

}

以上便是使用Object 数据类型的来进行动态绑定,但是与其指定为 Object 还不如暂时不指定类型,然后决定具体使用何种类型。

下面就真正开始归纳泛型的基本用法和简要的总结:

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *   @param   <T>

  *   泛型示例 主要是看简单泛型的基本语法格式

  */

public   class  Holder_3<T> {

public  Holder_3(T a){ this . a  = a;}

public  T get(){ return   a ;}

public   void  set(T a){ this . a  = a;}

public   static   void  main(String[] args) {

Holder h =  new  Holder();

Holder_3<Holder>   h3  =  new  Holder_3<Holder>(h); 

System. out .println( h3 );

Holder hh =  h3 .get();

System. out .println(hh);

/* h3 .set("cooole"); //h3已经限定了其类型为Holder所以其他类型

h3 .set(new Integer(1));//比如String、Integer还是其他非Holder类  

都是不允许传进来类型参数的

      */

}

private  T  a ;

}

    Java泛型的核心概念:告诉编译器像使用什么类型,然后编译器帮你处理一切细节。在使用泛型时只需要指定它们的名称和类型即可。

在通常的系统开发当中,我们一定会遇到经常使用一次方法调用,就能返回多个对象的情况。但是return 只能返回一个对象。因此解决办法就就是:创建一个对象用这个对象来持有想要返回的多个对象。当然在每次需要的时候就会专门创建这样的类。万一系统庞大,那么就会出现各种各样的问题,类就会过于庞大,同时也不能很好的体现 OOP 的思想。有一种好的设计思想就是利用泛型来一次性来解决问题,同时也可以利用泛型来在编译期间进行检查确保类型的安全,同时泛型也不是十全十美的会有一定的性能下降。

在学习泛型的时候要注意到元组的问题,实质上在通常的情况下可以这样来理解,元组就是类型的列表外加<>

泛型也可以运用到接口上这样的接口称为泛型接口。

public   interface   GenericsInterface <T> {T method();} 是被允许的。

Java泛型“基本类型无法作为类型参数”的局限性已经是众所周知了,不过从 J2SE5

就具备了自动打包和解包的功能,所以基本的数据类型会也应该转换为对应的对象类型,再进行下一步的处理。

    

   泛型到现在的示例为止都是运用于整个类。但是从实际开发的过程中的规范或者说是好的习惯来看, 在能力的范围内,能用泛型方法代替泛型类的就应该尽量代替 。所以在这里就要讲到泛型方法的基本语法结构和一些必须非常熟悉的基本原理,这样才能站在更高的层次上来理解Java 泛型。

    先介绍语法:

package  cn.ccsu.cooole.generics;

/**

  *   @author   CoolPrince

  *   本案例的输出情况:

  *   SampleName:    String    |    Name:    java.lang.String

  *   SampleName:    Float    |    Name:    java.lang.Float

  *   SampleName:    Integer    |    Name:    java.lang.Integer

  *   SampleName:    Integer    |    Name:    java.lang.Integer

  *   从输出情况可以了解到两个重要的点:

  *   1:从i0和i1这两个地方我们就能很清晰地看到: Java泛型“基本类

  *      型无法作为类型参数”的局限性已经是众所周知了,不过从J2SE5就具

  *      备了自动打包和解包的功能,所以基本的数据类型会也应该转换为对应

  *      的对象类型,再进行下一步的处理。

  *    2:定义泛型方法只需要将泛型   参数列表   至于返回值之前

  */

public   class  GenericsMethods {

// 简单的示例输出对应的类名

public  <T>  void  method(T x) {

System. out .println( "SampleName:  "  +  x.getClass().getSimpleName()

"  |  Name:  "  + x.getClass().getName());

}

public   static   void  main(String[] args) {

GenericsMethods gm =  new  GenericsMethods();

String s =  "cooole" ;

int  i0 = 23;

Integer i1 = 13;

gm.method(s);

gm.method(10.F);

gm.method(i0);

gm.method(i1);

}

}

     值得特别注意的是: 如果static 方法需要使用泛型能力,就必须使其成为泛型方法。

当使用泛型类时,必须在创建对象的时候指定参数的值,而使用泛型方法时,通常不必指明参数的类型,因为编译器有个功能叫做:类型参数推断,此时编译器会为我们找出具体的类型。

     类型参数推断举例:

package  cn.ccsu.cooole.generics;

import  java.util.ArrayList;

import  java.util.HashMap;

import  java.util.LinkedList;

import  java.util.List;

import  java.util.Map;

/**

  *   @since   2009 - 7

  *   @author   CoolPrince

  *   @describe :

  *   该示例展示的是类型参数推断的一个简单的示例

  *   以及泛化方法的基本形式

  *   泛型方法使得该方法能够独立于类而产生变化,先辈给的一个经验就是:

  *   无论何时何地,只要你能做到的,就应该使用泛型的方法。也就是说能够将

  *   用泛型方法取代整个类的就应该使用泛型方法。这样做会使得事情更加清晰明了。

  *   另外值得注意的是:对于一个static的方法而言,无法访问泛型类的类型参数,

  *   所以如果static方法需要使用的能力,就必须使其成为泛型方法。

  *   就类型参数推断而言,那这个例子来讲,假设用户不知道GuessParaType中的方法

  *   或者说是隐含的功能,花的时间也没有减少。工作效率也没有得到提升

  *  

  *   需要特别特别注意的是:   类型推断只对赋值操作有效,其他时候并不起作用

  *   (比如作为参数传递给方法)。   所以function(GuessParaType.list())

  *   的用法编译时候无法获得通过。  

  *   如果是定义在该方法类的内部就需要使用this而静态方法则需要添加类名。

  */

public   class  GuessParaType {

public   static  <K,V> Map<K,V>  map(){

return   new  HashMap<K,V>();

}

public   static  <T> List<T> list(){

return   new  ArrayList<T>();

}

public   static  <T> LinkedList<T> linkedList(){

return   new  LinkedList<T>();

}

public   static   void  function(List<?  extends  Holder> s){

System. out .println(s.getClass());

}

public   static   void  main(String[] args) {

@SuppressWarnings ( "unused" )

Map<String,List<Integer>>  sls = GuessParaType. map ();

@SuppressWarnings ( "unused" )

List<Boolean>  l = GuessParaType. list ();

@SuppressWarnings ( "unused" )

LinkedList<String>  ll = GuessParaType. linkedList ();

System. out .println(sls.getClass());

//function(GuessParaType.list());//此中用法不能用上面有说明

function (GuessParaType.<Holder_T> list ()); //显示的类型说明

}

}

class  Holder_T  extends  Holder{}

     

c++ 一样 Java 也具有可变参数列表:

package  cn.ccsu.cooole.generics;

import  java.util.ArrayList;

import  java.util.List;

/**

  *  

  *   @author   CoolPrince

  *   @描述  

  *   可变参数的泛型

     *   输出:

  *   [,   a,   b,   c,   d,   e,   f,   g,   h,   j,   k,   l,   m,   n,   o,   p,   q,   r,   s,   t,   u,   * v,   w,   x,   y,   z]

  */

public   class  GenericVarargs {

    public   static  <T>  List<T>  makeList(T ...args){

   List<T> t =  new  ArrayList<T>();

    for (T item : args )

   {

   t.add(item);

   }

    return  t;

   }

public   static   void  main(String[] args) {

     List<String>  list =  makeList ( "abcdefghjklmnopqrstuvwxyz" .split( "" ));

     System. out .println(list);

}

}

    

泛型运用于接口当中,只要泛型方法在参数声明为接口,都可以用到该接口的泛型实现的类的对象。方法的泛型在运用于接口的同时也可以运用到内部类当中来,甚至是匿名的类当中。比如一个非常经典的例子银行出纳员服务的问题(这是一个典型的泛型匿名类):

return   new  Generator<Teller>(){

public  Teller next() {

return   new  Teller();

}

};

    

"好你个擦除,尽然让我这番折腾。 "在学习泛型一章的时候,最感到晕的就是擦除,这个是 Java 泛型的难点,也是泛型的重点的重点。竟然没弄清这个感觉就是学习 Java 遇到了一个大大的障碍。学这个的时候很晕,可以这样说吧,以前很少见到这种情况,主要是没有什么实践经验,这个内容很抽象,初一看,看不出什么名堂,但是硬着头皮学完了这个泛型,就可以说才真正的开始了解(我的实战项目很少,到目前为止觉得自己连菜鸟还不算,也就是一个还为成鸟的蛋,所以不敢大言熟练)。 " 好你个擦除! "

首先擦除初一看很神秘,在一看的确很神秘,作死的看还是能解开她的一层神秘的面纱。

探秘擦除:

1:了解先决条件。什么是擦除?在使用泛型的这个场景的时候,任何具体的类型信息都无法看到,而你唯一知道的就是你在使用一个对象。这个是我个人的理解,可以想象一下,为什么叫擦除。从这问题出发,我个人觉得,就是这个 T 会被别的类型反复的覆盖,主要原因是 Java 运行的是 .class  字节码文件。而且又是一门纯面向对象的语言。因而会产生引用的现象,然而有些时候我们只需要运用到这个通用的方法(只是参数是不定的可以被反复运用),在同一个方法中的同一个参数列表的位置,每执行一次对其后续的传入对象的影响,每个对象的引用传进来时也不受先前的对象的引用的影响,所以就把参数列表的参数进行一次擦除。而在这个过程当中参数列表中的参数只是占了个符号进行标识。(这个是我个人的理解,术语还欠专业,只是为了讲明白这个意思)。

2:分析需求。为什么要用擦除?擦除的目的实质上时很简单了,从 JDK1.4 JDK1.5  的版本内容飞跃, Sun 公司历时 5 年将类库和虚拟机进行完善的这个角度上来分析,也可以从将 JDK1.5 改名为 JDK5.0 。或者从微软的 windows 操作系统来对比。就不难看出,擦除的最初目的就是为了 向后兼容 。因为此前也开发了不少产品,擦除的目的就是让泛型和非泛型进行结合。而擦除的机制使得先后迁移或者说是兼容成为可能,到目前为止也是最好的一种解决方案,以后的话或许还是最好的,但是就不敢说绝对最好了,毕竟技术还在前进。

3:分析优缺点缺点。擦除有哪些好的理由让它存在比成为设计类库的一个重要机制?还有擦除有哪些实际的问题?先回答第一个:擦除的优点上面 2 有讲到,主要从非泛化到泛化的转变过程,以及在不破坏现有的类库的情况下,将泛型融入到 Java 里边来。擦除使得现有的非泛化的客户端代码能在不改变的情况下继续使用,直到客户端重写了这些代码。例子很容易举,假设有家银行系统是 Java 的非泛型写的,但是它系统又要运行,对于银行的客户或者其他不同权限的系统使用者来讲,要不改变原来的东西,但是在升级的时候将代码重写时就适当运用泛型进行一个过渡。再是第二个问题:擦除的代价也是显而易见的,泛型不能显示的运用于运行时的类型当中。因为此时所有的关于参数类型的信息已经丢失。

讲到这先看个具体示例:验证擦除后的类型信息的获取。

  /**

  *   @author   CoolPrince

  *   @since   2009 - 7

  *   @描述   :擦除的神秘之处

  *   output:~

  *   c1   =    class   java.util.ArrayList

  *   c2   =    class   java.util.ArrayList

  *   c1==   c2?true

  *   class   java.util.ArrayList

  *   :~

  *   形式可以是:Class   c2   =   new   ArrayList <String> ().getClass();

  *   虽然可以申明为:ArrayList.class

  *   但是不能为   Class   c2   =   new   ArrayList <String> ().class;

  *   也不能为ArrayList <String> .class();

  */

public   class  ErasedTypeEquivalence {

/**  

  *   @param   args

  */

public   static   void  main(String[] args) {

Class c1 =  new  ArrayList<Integer>().getClass();

Class c2 =  new  ArrayList<String>().getClass();

     print ( "c1 =  " + c1);

     print ( "c2 =  " + c2);

     print ( "c1== c2? " + (c1== c2));    

     print (ArrayList. class );

}

public   static   void  print(Object  o){

System. out .println(o);

}

}

从上面的注释可以看到,c1 c2 class 都是 java.util.ArrayList ,所以关于参数 String Integer 的类型信息已经丢失。

c++ 模板当中会有这样的几句话:

template <class   T >

{

    T  object;

     public  mothod(){object.f();}

};

c++ 中只管定义,在调用的时候会自动检查 T 类型是否拥有 f() 方法如果没有就会发出异常。

而在Java 中不能支持这种形式,编译器报错。这个也是 c++ 在类型的参数化方面比 Java 强大的主要原因之一。

那么Java 中如何实现上述的 C++ 内容呢?具体请看代码及其注释。

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *   @param   <T>

  *  

  *   在这个示例当中首先要告诉编译器T是什么类型,下面两句  

  *   FunMethodExtendsClass   fmec=   new   FunMethodExtendsClass();  

  *   new   HasFunMethod <FunMethodExtendsClass> (fmec);

  *   其中传入参数fmec告诉编译器其传的是FunMethodExtendsClass

  *  

  *   如果将public   HasFunMethod(T   x){object   =   x;}去掉的话

  *   并且将构造器改为默认的,那么调用hf.testFun()编辑器不能检查到错误

  *   但是运行的时候就会有空指针异常(java.lang.NullPointerException)

  */

public   class  HasFunMethod<T  extends  FunMethodClass> {

private  T  object ;

public   void  testFun() {

object .fun();

}

public  HasFunMethod(T x) {

object  = x;

}

public   static   void  main(String[] args) {

FunMethodExtendsClass fmec =  new  FunMethodExtendsClass();

HasFunMethod<FunMethodExtendsClass> hf = 

new  HasFunMethod<FunMethodExtendsClass>(fmec);

hf.testFun();

fmec.fun();

}

}

class  FunMethodClass {

public   void  fun() {

System. out .println( "fun()" );

}

}

class  FunMethodExtendsClass  extends  FunMethodClass {}

     验证泛型数据的擦除后类型的一致性:

package  cn.ccsu.cooole.generics;

import  java.util.ArrayList;

import  java.util.List;

/**

  *   @author   CoolPrince

  *  

  *   @param   <T>

  *  

  *   将对象传入List   尽管编译器无法知道有关create()中的T的任何类型的信息,

  *   但是它仍然可以确保   *   你放位置到list的对象具有T类型,使其适合

  *   ArrayList <T> ,因此,即使擦除在方法或者类的内部移除了有关实际的类型信息,

  *   编译器仍然可以确保在方法或者类中   使用的类型的内部一致性。

  *  

  */

public   class  FillList<T> {

public  List<T> create(T t,  int  n) {

List<T> list =  new  ArrayList<T>();

for  ( int  i = 0; i < n; i++) {

list.add(t);

}

return  list;

}

public   static   void  main(String[] args) {

FillList<String> stringMake =  new  FillList<String>();

List<String> list = stringMake.create( "cooole" , 4);

System. out .println(list);

FillList<Make> make =  new  FillList<Make>();

List<Make> list1 = make.create( new  Make(), 3);

System. out .println(list1);

}

}

class  Make {

@Override

public  String toString() {

return   "make" ;

}

}

     既然擦除有类型信息丢失的现象,我们是否有补救措施?答案当然是有。

下面是通过引入类型标签解决判定是否可以动态的实例化类的对象。

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *  

  *   输出结果:

  *   true

  *   true

  *   false

  *   true

  *  

  *   描述:泛型因为擦除的原因其类型信息就已经擦除了,然而在引入类型标签的话,

  *   就可以转而使用动态的isInstance()来进行类型的实例化的判断。编译器将确保

  *   类型标签可以匹配泛型参数。

  *

  */

public   class  ClassTypeCapture<T> {

Class<T>  kind ;

public  ClassTypeCapture(Class<T> kind){

this . kind  = kind;

}

public   boolean  f(Object o){

return   kind .isInstance(o);

}

public   static   void  main(String[] args) {

ClassTypeCapture<Building>  ctc1 =  new  ClassTypeCapture<Building>(Building. class );

System. out .println(ctc1.f( new  Building()));

System. out .println(ctc1.f( new  House()));

ClassTypeCapture<House> ctc2 =  new   ClassTypeCapture<House>(House. class );

System. out .println(ctc2.f( new  Building()));

System. out .println(ctc2.f( new  House()));

}

}

class  Building{}

class  House  extends  Building{} 

     

    既然判定对象的是否可以实例化的问题已经解决下面的问题就是实例化对象的操作。

package  cn.ccsu.cooole.generics;

public   class  ClassInstantiateGenericType {

public  ClassInstantiateGenericType() {

}

public   static   void  main(String[] args) {

@SuppressWarnings ( "unused" )

ClassAsFactory<Employee> fe =  new  ClassAsFactory<Employee>(

Employee. class );

System. out

.println( "new ClassAsFactory<Employee>(Employee.class)   is Succeeded" );

try  {

@SuppressWarnings ( "unused" )

ClassAsFactory<Integer> fi =  new  ClassAsFactory<Integer>(

Integer. class );

catch  (Exception e) {

System. out

.println( "new ClassAsFactory<Integer>(Integer.class)  is failed" );

}

}

}

class  ClassAsFactory<T> {

x ;

public  ClassAsFactory(Class<T> kind) {

try  {

kind.newInstance();

catch  (Exception e) {

throw   new  RuntimeException(e);

}

}

}

class  Employee {

}

这个示例运解决的是一个Java 创建类型实例的一个运用例子,这个是工厂模式。

而工厂的对象就是Class 对象,所以使用类型标签是十分合理的,而此时只需要将 new Intstance()来实例化并创建这个类型的新对象。

Java泛型的参数要求所传递类型的构造器是公有默认的构造器。而上面示例 Integer 类没有默认的构造器。 会报J ava.lang.InstantiationException 的错误。  而这个错误并不是在编译期间捕获的所以不建议使用这种方式,下面是这个示例的改良。

   package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *   描述:显示工厂的方法解决Java进行泛型类的实例化的问题。

  *  

  *   主要是限定其类型使得只能接受实现了这个工厂的类

  *   此时已经获得了编译期间的检查。所以也是比较稳定和安全的

  */

public   class  FactoryConstaint {

public   static   void  main(String[] args) {

new   Foo2<Integer>( new  IntegerFactory());

new  Foo2< Widget >( new   Widget .Factory());

}

}

interface  FactoryInterface<T>{

T create();

}

class  Foo2<T>{

@SuppressWarnings ( "unused" )

private  T  x ;

public  <F  extends  FactoryInterface<T>> Foo2(F factory){

x  = factory.create();

}

}

class  IntegerFactory  implements  FactoryInterface<Integer>{

public  Integer create() {

return   new  Integer(10);

}

}

class   Widget {

public   static   class  Factory  implements  FactoryInterface< Widget >{

public   Widget  create() {

return   new   Widget ();

}

}

}

   既然显示工厂可以解决此问题,那么还有其他的解决途径没有?模板方法设计模式。

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *   模板模式解决Java泛型类额实例化问题

  *   通过这种方式可以获取类型的信息

  */

public   class  CreatGenerics {

public   static   void  main(String[] args) {

Createor c =  new  Createor();

c.f();

}

}

abstract   class  GenericWithCreate<T>{

final  T  elements ;

GenericWithCreate(){ elements  = create();

}

abstract  T create(); 

}

class  ClassX{}

class  Createor  extends  GenericWithCreate<ClassX>{

@Override

ClassX create() {

return   new  ClassX();

}

public   void  f(){

System. out .println( elements . getClass ().getSimpleName());

}

}

 泛型在数组中的运用: 一般的解决方案都是在任何想要创建泛型数组的地方都用 ArrayLis 进行替代。这样既能获得数组的行为也能获得由编译器提供的编译器类型的安全。

  直接创建数组不会发出任何警告。却也永远无法创建这个确切的数据类型数组,直接进行转化就会抛出异常。

 有一种方式。也是唯一的一中方式就是:利创建一个被擦除类型的新数组。然后转型。

具体示例见下文:

private  T[]  array ;

public  GenericsArray( int   size ){

array  = (T[])  new  Object[size];

}

这种形式是不行的尽管它在初学者看来和直观,当其对返回值进行转型的时候就会有一个叫做ClassCastException; 此时实际运行的是 Object[];

因为有了擦除,数组的运行时类型就只能是Object[], 如果我们立即将其转型为 T[], 那么编译期间的实际类型就会丢失,而编译器可能会错过某些潜在的错误检查。所以最好的方式是在集合内部使用 Object[], 然后当你使用数组元素的时候,添加一个 T 的转型。

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *

  *   @param   <T>

  *  

  *   输出:   [Ljava.lang.Object;@de6ced

  *   所以还是存在类型的丢失问题,当调用这个gai.getArray()的其他方法的时候

  *   编译器也会报告异常。

  */

public   class  GenericsArray2<T> {

private  Object[]  array ;

@SuppressWarnings ( "unchecked" )

public  GenericsArray2( int  size){

array  = (T[])  new  Object[size];

}

@SuppressWarnings ( "unchecked" )

public  T[] getArray(){

return  (T[]) array ;

}

public   void  put( int  index, T  item){

array [index]  = item;

}

public   static   void  main(String[] args) {

GenericsArray2<Integer> gai =  new  GenericsArray2<Integer>(10);

for ( int  i = 0;i<10;i++){

gai.put(i, i);

}

    System. out .println(gai.getArray());

}

}

   难道真的没有其他办法来解决这一难题。

先看看下面的反射再说:

    package  cn.ccsu.cooole.generics;

import  java.lang.reflect.*;

/**

  *   @author   CoolPrince

  *

  *   @param   <T>

  *  

  *   输出结果:

  *    class   [Ljava.lang.Integer;

  *    0   1   2   3   4   5   6   7   8   9

  *   

  *    类型标记Class <T> 被传送到构造器中,以便恢复,使得我们可以创建需要的实际的类型的数组

  *    尽管@SuppressWarnings("unchecked")压制警告,一旦获得实际的类型就可以进行返回

  *   

  */  

public   class  GenerArrayWithTypeToken<T> {

@SuppressWarnings ( "unused" )

private  T[]  array ;

@SuppressWarnings ( "unchecked" )

public  GenerArrayWithTypeToken(Class<T> type,  int  size) {

array  = (T[]) Array. newInstance (type, size);

}

public   void  put( int  index, T item) {

array [index] = item;

}

public  T get( int  index) {

return   array [index];

}

public  T[] getArray(){

return  (T[]) array ;

}

public   static   void  main(String[] args) {

GenerArrayWithTypeToken<Integer> gawti =  new   GenerArrayWithTypeToken<Integer>(

Integer. class , 10);

for ( int  i = 0;i<10;i++){

gawti.put(i, i);

}

Integer [] ia  = gawti.getArray();

System. out .println( ia .getClass());

for ( int  i = 0;i<10;i++){

   System. out .print( " " ia [i]);

}        

}

}

既然Java 的泛型可以如此神通广大,我们有什么方法对其进行一定的限制提高它的性能呢 ? 答案便是边界!

package  cn.ccsu.cooole.generics;

import  java.awt.Color;

/**

  *   @author   CoolPrince

  *

  *   @param   <T>

  *  

  *这个事例主要是分析边界及其继承关系的相关理论。

  *   边界实质上就是在泛型上设置限制条件。这个限制强制了所要使用的类型范围

  *   但是一个潜在的好处就是可以按照自己定义的边界来调用方法。比如说:

  *   int   weigth(){

  * return   item.weigth();

  * }

  *在此时已经保障了inem.weigth()的存在性。   因为这条语句

  *<T extends Dimension & HasColor & Weigth>

  *要求必须实现Weight接口

  *这个地方也有个小的策略,就是先将类放到extends的后面然后就是接口口,尽量将方

* 法多的,

  *或者是子类放到靠前面一些这样就会有一定的效率提高,其次限制时没有implements

 * 而是将

  *extends代替,这个是Java的设计者考虑到extends能够更好的表意所以统一采用这种 * 形式

  *此时的extends和平时的用法是完全不同的。

  */

public   class  GenericInheritBaseBounds<T> {

public   static   void  main(String[] args) {

@SuppressWarnings ( "unused" )

Solid<Bounded> sb =  new  Solid<Bounded>( new  Bounded());

System. out .println(sb.getX());

System. out .println(sb. weigth ());

System. out .println(sb.getItem().getClass());

}

}

interface  HasColor{java.awt.Color  getColor();}

interface  Weigth{ int  weigth();}

class  HoldItem<T>{

item ;

HoldItem(T item){ this . item  = item;}

T getItem(){ return   item ;}

}

class  Dimension{ public   int   x , y , z ;}

class  Colored<T  extends  HasColor>  extends  HoldItem<T>{

Colored(T item){ super (item);}

}

class  ColorDimension<T  extends  Dimension & HasColor>  extends  Colored<T>{

ColorDimension(T item) { super (item);}

int  getX(){ return   item . x ;}

}

class  Solid<T  extends  Dimension & HasColor & Weigth>  extends  ColorDimension<T>{

Solid(T item) { super (item);}

int   weigth (){

return   item .weigth();

}

}

class  Bounded  extends  Dimension  implements   HasColor,Weigth{

public  Color getColor() {

return  Color. black ;

}

public   int  weigth() {

return  10;

}

}

 

泛型通配符:

泛型一个主要目标是将运行时可以发现插入不正确的数据类型的错误信息检测移入到编译期。

下面的几个示例就是讨论通配符的相关的问题:主要是总结那些情况下可以用,那些情况下有缺陷,哪些情况下可以进行优化。

首先简述数组的一种特殊的行为:可以向导出类型的数组赋予基类型数组引用。假设有这样一种情况:基类蔬果(Fruit), 其有两个子类苹果( Apple )、桔子( Orange , 另外又有苹果类又有一个子类 --- 李子苹果( PlumApple )的杂交苹果。

Fruit []  fruit  =  new  Apple[10]; 那么 fruit[0] =  new  Apple();

fruit[0] =  new  PlumApple(); 都是合法的因为他们都是Apple 的子类,然而 new   Fruit (),或者是非苹果的子类都会报告数组存储错误的异常。为什么会有这种情况的出现呢?虽然有个 Fruit []  fruit 数组的引用,在编译期间就算你new Orange() 也不会报告异常,但是在运行期间 Java 还是会根据数组的基本机制将其处理成 Apple[], 因此在数组中放入异构型的数组时就会有异常。数组有一个重大的好处就是他可以保留有关他们的包含的对象的类型的信息规则。一般这样说,数组对其持有的对象是有意识,因此在编译期间一定要做检查,否则就会造成滥用的情况的发生。那么用 List<Fruit> f =  new   ArrayList <Apple>(); 来可以吗?不行两个泛化的参数必须一致, List<Fruit> 的对象将持有所有 Fruit 的所有的Fruit 对象也包括 Apple 。但是这里的情况 List<Fruit> 的对象和 List< Apple > 的对象时不同的,这个地方和多态是有区别的,从多态的单个来讲 Fruit f = new Apple(); 是可以的允许的而且此时持有的也是 Apple 的对象,加了个 List 容器就不同了。为什么这样讲呢?理论上时这样讲的:这里是讲的容器的类型而不是容器所持有的类型。也就是上面那句话所描述的那样。

现在假设有另一种情况假设缴入了边界的限制又会怎样呢?形式如 List<?  extends  Fruit> = new ArrayList<Apple>();又能行吗?你先自己编一下。答案告诉我们这种方式是行不通的。此时定义为 new ArrayList<Apple>() ,如果可以的话那么  new ArrayList<Orange>() 也是安全可行的,但是这样的话岂不是很混乱。在不知道 List 持有什么样的类型时候是不能向其中添加任何对象的(除了 null )。所以必须明确 List 持有对象的类型。那么这样的形式岂不是毫无用处。实际上也不补完全是这样,如果你调用 Fruit 类的方法还是不会报异常,也是安全的,因为此时在 List 持有的对象中至少具有 Fruit 的类型,此时编译器是允许这样做的。就拿 List<?  extends  Fruit> 来说,编译器并不知道是Fruit 的具体的哪个类。 extends  Fruit 意味着可以是任何类型的事物,同时编译器又无法验证它的安全性。这个地方还是有严格限制的。

说白了泛型要知道具体的持有的对象的参数,至少也要通过反射来告诉编译器是个什么东东,在泛化的过程中利用类型标签来实现工厂模式,模板模式来解决泛型是非常有用的。

既然有继承的统配符是否有超类的通配符来进行类型的限定?

超类的通配符的语法<? super SuperClassName>,<? s extends  SubClassName> 是上界,而超类的通配符就是我们所说的下界。另外还有个叫无界 <?>, 下面也将讲到。

package  cn.ccsu.cooole.generics;

import  java.util.Arrays;

import  java.util.List;

/**

  *  

  *   @author   CoolPrince

  *

  *   输出:

  *   fun1()   f.getClass():   class   cn.ccsu.cooole.generics.Apple

  *   fun1()   a.getClass():   class   cn.ccsu.cooole.generics.Apple

  *   fun2()   f.getClass():   class   cn.ccsu.cooole.generics.Fruit

  *   fun3()   f.getClass():   class   cn.ccsu.cooole.generics.Fruit

  *   fun3()   a.getClass():class   cn.ccsu.cooole.generics.Apple

  *  

  *   先从输出来对其原理进行深层次分析:

  *   fun1()中调用的readExact();参数已经进行类限制,并没有进行通配

  *   此时编译编译器就会根据实际的类型进行调用和传参,只是此时在编译期间

  *   进行强制的类型检查,避免了许多的错误,而不会出现可以是任何事物的

  *   情况而使得编译器不知道如何处理。

  *   在fun2()中由于Reader <Fruit>   fruitReader   =   new   Reader <Fruit> ();

  *   的限制而无法将Apple   作为类型进行检查,此时的注释部分是不能运行的(编译期

  *   间就有错误)。

  *   在fun3()中则运用了?extends进行通配   此时只要是Fruit的子类就可以传进来只

  *   要将其返回值为Fruit就行,其实此时已经是Apple的引用。故而有上述的显示结果。

  */

public   class  GenericReading {

static   class  CovariantReader<T> {

T readCovariant(List<?  extends  T> list) {

return  list.get(0);

}

}

static   class  Reader<T> {

T readExact(List<T> list) {

return  list.get(0);

}

}

static  List<Apple>  apples  = Arrays. asList ( new  Apple());

static  List<Fruit>  fruits  = Arrays. asList ( new  Fruit());

static   void  fun1() {

@SuppressWarnings ( "unused" )

Apple a =  readExact ( apples );

Fruit f =  readExact ( fruits );

f =  readExact ( apples );

System. out .println( "fun1() f.getClass(): "  + f.getClass());

System. out .println( "fun1() a.getClass(): "  + a.getClass());

}

static   void  fun2() {

Reader<Fruit> fruitReader =  new  Reader<Fruit>();

Fruit f = fruitReader.readExact( fruits );

System. out .println( "fun2() f.getClass(): "  + f.getClass());

// Fruit a = fruitReader.readExact(apples);

}

static   void  fun3() {

CovariantReader<Fruit> crf =  new  CovariantReader<Fruit>();

Fruit f = crf.readCovariant( fruits );

Fruit a = crf.readCovariant( apples );

System. out .println( "fun3() f.getClass(): "  + f.getClass());

System. out .println( "fun3() a.getClass():"  + a.getClass());

}

public   static   void  main(String[] args) {

fun1 ();

System. out .println();

fun2 ();

System. out .println();

fun3 ();

}

static  <T> T readExact(List<T> list) {

return  list.get(0);

}

}

    <?>可以笨认为是一种装饰,但是它仍然具有一定的实际价值,在实际看来: " 我想用Java 的泛型编写这段代码 , 我这里并不是要用原生类型,但是在当前的情况下泛型可以持有任何类型。 "

     泛型通配符,以List 为例 List<Object> List<?> 是有区别的, List 实际上时表示 " 持有任何 Object 类型的原生 List", List<?> 表示 " 具有某种特定类型的非原生List 只是我们不知道那种类型是什么。 "

    当泛型过于受限的时候就应该考虑通配符,这主要取决于泛型的参数中返回类型确定的返回值。

因此运用确切类型来替代通配符的好处是:可以用泛型的参数来处理更多的事情,但是使用通配符使得你必须接受范围更加宽的参数化类型作为参数。

那么什么时候使用通配符比使用原生的更好或者说是更加需要。那就是我们通常所说的捕获转换这个还真的是非常需要。

捕获转化的定义: 如果向一个使用<?> 的方法传递原生类型,那么是对于编译器来说,可能会推断出实际的类型参数 ( 运用反射就可以得到 ) ,使得这个方法可以回转并调用另一个使用这个确切类型的方法(事实上泛型参数擦除的是第一边界,这个情况是一定的)。

下面这个例子足以对其进行说明( 详见上面一段话结合程序进行理解,也对注释特别留意,并慢慢体会 )

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *   @param   <T>

  *   泛型示例  

  */

public   class  Holder_3<T> {

public  Holder_3(T a){ this . a  = a;}

public  T get(){ return   a ;}

public   void  set(T a){ this . a  = a;}

public  Holder_3(){}

public   static   void  main(String[] args) {

Holder h =  new  Holder();

Holder_3<Holder>  h3 =  new  Holder_3<Holder>(h); 

System. out .println(h3);

Holder hh = h3.get();

System. out .println(hh);

/*h3.set("cooole"); //h3已经限定了其类型为Holder所以其他类型

  h3.set(new Integer(1));//比如StringInteger还是其他非Holder类   型都是不允许传进来类型参数的

      */

}

private  T  a ;

}

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *   捕获转化举例:

  *   先看输出结果:

  *   Integer

  *Integer

  *Object

  *Double

  *   再看   main方法:

  *   Holder_3   h3t   =   new   Holder_3 <Integer> (23);

  *   定义一个Holder_3   h3t   类型参数为Integer

  *   此时调用方法method1();就会输出类型名:Integer

  *   调用方法menthod2()的时候里面传进来方法method1   的参数

  *   此时就会被擦除从而带入到method2中,故而会有上面的结果。

  *   这个过程通过参数的传递来进行捕获转化。此时的通配符就派上了用场

  *  

  */

public   class  CaptureConversion {

static  <T>   void  method1(Holder_3<T> holder){

T t = holder.get();

System. out .println(t.getClass().getSimpleName());

}

static   void  method2(Holder_3<?> holder){

method1 (holder);

}

@SuppressWarnings ( "unchecked" )

public   static   void  main(String[] args) {

Holder_3 h3t =  new  Holder_3<Integer>(23);

method1 (h3t);

method2 (h3t);

Holder_3 h3t2 =  new  Holder_3();

h3t2.set( new  Object());

method2 (h3t2);

Holder_3<?> wildcard =  new  Holder_3<Double>(23.0);

method2 (wildcard);

}

}

泛型没有消除对对象转型的需要。

package  cn.ccsu.cooole.generics;

public   class  FixedStack<T> {

private   int   index   = 0;

private  Object[]  storage ;

public  FixedStack( int  size){ 

storage  =  new  Object[size];

}

public   void  push(T item){

storage [ index ] = item;

index ++;

}

@SuppressWarnings ( "unchecked" )

public  T pop(){

return  (T)  storage [ index --];

}

public   static   void  main(String[] args) {

FixedStack<Integer>  ii =  new  FixedStack<Integer>(11);

for ( int  i = 1;i<=10;i++){

ii.push(2*i);

}

 ii.pop();

System. out .println(ii.pop().getClass().getSimpleName());

}

}

泛型没有消除对对象转型的需要,从pop ()中的强制转化,没转化前还是 Object

    实现参数化接口:

interface  Payable<T>{}

class  Emplyee  implements  Payable<Emplyee>{}

c lass   Hourly   extends  Emplyee  implements  Payable< Hourly >{}

在这个实例当中最后一条语句由于擦除的原因,这两个变体会成为相同的借口而产生冲突。如果将 Payable<T> 的参数类型移除就能够通过编译。

对于泛型重载,由于擦除的缘故,重载类型的方法将产生类型相同的签名,当被擦除的参数不能产生唯一的参数列表时,必须提供有明显区别的方法名。

Java自限定类型:

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince

  *   自限定的基本语法如同:

  *   class   SelfBounded<T extends SelfBounded<T>>

  *   自限定限制只能强制作用于继承关系,如果使用自限定就必须了解这个

  *   类所用的类型参数将与使用这个参数的类具有相同的基类型。

  *  

  */

public   class  SelfBounding {

public   static   void  main(String[] args) {

A a =  new  A();

System. out .println(a.getClass());

a.set( new  A());

System. out .println(a.getClass());

a = a.set( new  A()).get();

System. out .println(a.getClass());

B b =  new  B(); 

System. out .println(b.getClass());

C c =  new  C();

c = c.setAndGet( new  C());

System. out .println(c.getClass());

}

}

class  SelfBounded<T  extends  SelfBounded<T>>{

element  ;

SelfBounded<T> set(T  arg ){

element  =  arg ;

return   this ;

}

T get(){ return   element ;}

}

class  A  extends  SelfBounded<A>{}

class  B  extends  SelfBounded<A>{}

class  C  extends  SelfBounded<C>{

C setAndGet(C arg){ 

set(arg);

return  get();

}

}

    其实Java 的自限定类型的价值在于他们可以产生协变参数类型——方法参数类型会随着子类而进行变化(类型雨来与严格也就是擦除第一边界所引起的),自限定类型还可以产生于基类类型相同的返回类型。

   

package  cn.ccsu.cooole.generics;

/**

  *  

  *   @author   CoolPrince   输出结果:  

  *   cn.ccsu.cooole.generics.ImplmentsGetter@61de33

  *   cn.ccsu.cooole.generics.ImplmentsGetter@61de33

  *  

  *   cn.ccsu.cooole.generics.ImplmentsGetter_1@ca0b6

  *   cn.ccsu.cooole.generics.ImplmentsGetter_1@ca0b6

  *  

  *   此时从输出可以见到我们所谓的动态绑定的效果

  *   这个时候就可以看到参数协变的效果。

  *   和普通的继承一样了,不信得话可以讲类型数去掉后在去观察结果

  *   已经重载了方法get();

  */

public   class  GenericsAndReturnType {

public   static   void  main(String[] args) {

ImplmentsGetter ig =  new  ImplmentsGetter();

test (ig);

System. out .println();

ImplmentsGetter_1 ig1 =  new  ImplmentsGetter_1();

test (ig1);

}

static   void  test(Getter g) {

@SuppressWarnings ( "unused" )

Getter res = g.get();

System. out .println(res);

@SuppressWarnings ( "unused" )

GenericGetter gg = g.get();

System. out .println(gg);

}

}

interface  GenericGetter<T  extends  GenericGetter<T>> {

T get();

}

interface  Getter  extends  GenericGetter<Getter> {

}

class  ImplmentsGetter  implements   Getter  {

public  Getter  get () {

return   this ;

}

}

class  ImplmentsGetter_1  extends  ImplmentsGetter {

}

动态类型安全问题分析:

    受检查的容器在你试图插入类型不正确的数据时就会,抛出ClassCastExcption, 这与泛型之前的原生的容器形成了鲜明的对比,对于后者来说,当你将对象从容器中取出的时候才会通知你出现了问题,此时尽管你直到出现了问题但是你不知道问题在哪,如果使用受检查的容器就会在编译期间发现插入不良的对象。

 

既然泛型在编译期间的安全问题自行可以解决,那么在运行期间的异常又该如何处置呢?

可以采用这种方式:

 Class TestClass<T,E extends Exception>{

   public  void f() throws E{}

}

当然参数E 也可以是其他自定义继承非 Throwable 的类。

其他的和上面的基本相同。

   在擦除忘记其比本类型,所以泛型类不能直接继承自一个泛型的参数。

故而产生了另外一种解决方案,说白了就是用类来继承混合接口的实现,来实现混型的机制。

例子很简单,只是代码稍微有些长:

  package  cn.ccsu.cooole.generics;

import  java.util.Date;

/**

  *   @author   CoolPrince

  */

public   class  Mixins {

public   static   void  main(String[] args) {

Minin minin1 =  new  Minin(), minin2  =  new  Minin();

minin1.set( "测试 String 1" );

minin2 .set( "测试 String 2" );

System. out .println(minin1.get()+ " " +minin1.getSerialNumber()+minin1.getStamped());

System. out .println( minin2 .get()+ " " + minin2 .getSerialNumber()+ minin2 .getStamped());

}

}

interface  TimeStamped{ public   long  getStamped();}

interface  SerialNumbered{ public   long  getSerialNumber();}

interface  Basic{

public  String get();

public   void  set(String val);

}

class  TimeStampImp  implements  TimeStamped{

private   final   long   timeStamp ;

TimeStampImp(){

timeStamp  =   new  Date().getTime();

}

public   long  getStamped() {

return   timeStamp ;

}

}

class  BasicImp  implements  Basic{

   

String  value ;

public  String get() {

return   value ;

}

public   void  set(String val) {

value  = val;

}

}

class  SerialNumberedImp  implements  SerialNumbered{

private   static   long   counter = 1;

public   long  getSerialNumber() {

return   SerialNumber ;

}

private   final   long   SerialNumber  =  counter ++;

}

/**

  *  

  *   @author   CoolPrince

  *   典型的混型代理

  */

class  Minin  extends  BasicImp  implements  TimeStamped,SerialNumbered{

private  TimeStamped  timeStamp  =  new  TimeStampImp(); 

private  SerialNumberedImp  serialNumberedImp  =  new  SerialNumberedImp();

public   long  getStamped() {

return   timeStamp .getStamped();

}

public   long  getSerialNumber() {

return   serialNumberedImp .getSerialNumber();

}

}

 似乎从上面的这个例子就好像与泛型搭不上边了,在这里先要特别注意泛型是数据类型的一个基本要素,下面介绍一下与泛型或者混型有关的几个基本的设计模式:

1:装饰器模式(这个模式在类库里用的非常多,不知你对装饰器有无了解,其中一个重要的库的主要思想就是装饰器模式,在尚学堂的视频里讲的那个管道就是装饰器模式的一种说法,我也是后来看源码才知道的),那就先不分析那些 Buffered Data ,留到下篇文章讲 I/O 的时候来分析部分源码来讲解装饰器模式。

先看个简单的例子:

      package  cn.ccsu.cooole.generics.decorator;

import  java.util.Date;

/**

  *  

  *   @author   CoolPrince   这个就是一个典型的装饰器设计模式的运用  

  *            仔细看是不是一层套一层。

  *           装饰器模式是使用的分层对象来态透明的向单个对象中添加责任。装饰器指定  

  *           包装在最初的对象的周围所有的对象具有相同的接口。某些事物是可以装饰的

  *           可以通过将其他类包装在可装饰的对象的四周,来将其分层。所以无论对象是  

  *           否有装饰你都可以拥有一个可以向对象发送公共消息集。当然装饰类也可以

  *           添加新的方法,但是这也是受限的。   混型和装饰器模式及其类似但是装饰器

  *           并不需要装饰器的继承结构(运用代理)

  *  

  */

public   class  DecorationMode {

public   static   void  main(String[] args) {

TimeStamped t =  new  TimeStamped( new  Basic());

TimeStamped t2 =  new  TimeStamped( new  SerialNumbered( new  Basic()));

SerialNumbered s =  new  SerialNumbered( new  Basic());

SerialNumbered s2 =  new  SerialNumbered( new  TimeStamped( new  Basic()));

System. out .println(t);

System. out .println(t2.getStamp());

System. out .println(s.getSerialNumber());

System. out .println(s2);

}

}

/**

  *  

  *   @author   CoolPrince   基类型

  */

class  Basic {

private  String  value ;

public   void  set(String val) {

value  = val;

}

public  String get() {

return   value ;

}

}

/**

  *  

  *   @author   CoolPrince   注意   protected这个修饰符表示的是具有包以及继承层次的子类拥有访问权限

  *           这个地方Decrator拥有类Basic   属于代理

  */

class  Decrator  extends  Basic {

protected  Basic  basic ;

public  Decrator(Basic basic) {

this . basic  = basic;

}

public   void  set(String val) {

basic .set(val);

}

public  String get() {

return   basic .get();

}

}

/**

  *  

  *   @author   CoolPrince   必须拥有构造器   getStamp()是新添加的   这个地方TimeStamped拥有类Decrator   属于代理

  */

class  TimeStamped  extends  Decrator {

public  TimeStamped(Basic basic) {

super (basic);

time  =  new  Date().getTime();

}

public   long  getStamp() {

return   time ;

}

private   final   long   time ;

}

class  SerialNumbered  extends  Decrator {

public  SerialNumbered(Basic basic) {

super (basic);

}

private   static   long   counter  = 1;

public   long  getSerialNumber() {

return   SerialNumber ;

}

private   final   long   SerialNumber  =  counter ++;

}

Java  并不支持潜在的类型机制,所以看上去不是非常的泛化。在c++ 中即便是一个不知道的类型  也可以调用它的方法只不过在传入类型参数的时候会进行检查看是否具有该方法。而 Java 对这个并不支持。它主要还是通过实现接口来继承类来实现这个机制的功能。尽管 Java 缺乏潜在类型的机制,但是还是有多种补救的措施: 反射 。上面也讲过通过反射来进行类型标签。

第二个就是将一个方法运用于序列(略)

适配器模式举例:

package  cn.ccsu.cooole.generics;

import  java.util.*;

/**

  *  

  *   @author   CoolPrince

  *   适配器模式举例:

  *   运行结果:

  *   Adpter.num   =   0

  *   Adpter.num   =   1

  *   Adpter.num   =   2

  *   Adpter.num   =   3

  *   Adpter.num   =   4

  *   Adpter.num   =   5

  *   Adpter.num   =   6

  *   Adpter.num   =   7

  *   这个例子主要是通过Collection来进行适配

  *  

  */

public   class  AdapterMode1 {

public   static   void  main(String[] args) {

         List<AdpterTmp> li =  new  ArrayList<AdpterTmp>();

         Fill2. fill ( new  AddableCollectionnAddapter<AdpterTmp>(li), AdpterTmp. class , 4);

         Fill2. fill (Adapter. collectionAdapter (li), AdpterTmp. class , 5);

          for (AdpterTmp i:li){

          System. out .println(i);

         }         

}

}

interface  Addable<T>{ void  add(T t);}

class  Fill2{

public   static  <T>  void  fill(Addable<T> addable,Class<?  extends  T> classToken, int  size){

for ( int  i = 0; i<size;i++){

try  {

addable.add(classToken.newInstance());

catch  (Exception e) {

    throw   new  RuntimeException(e);

}

}

}

}

class  AddableCollectionnAddapter<T>  implements  Addable<T>{

private  Collection<T>  c ;

AddableCollectionnAddapter(Collection<T> c){

this . c  = c;

}

public   void  add(T t) {

c .add(t);

}

}

class  Adapter{

public   static  <T>  Addable<T>  collectionAdapter(Collection<T> c){

return   new  AddableCollectionnAddapter<T>(c);

}

}

class  AdpterTmp{

static   int   counter  = 0; 

private   final   int   num  =  counter ++;

public  String toString(){

return   "Adpter.num = " num ;

}

}

这个例子描述了适配器创建了真正泛化的代码。

策略设计模式

package  cn.ccsu.cooole.generics;

import  java.math.BigDecimal;

import  java.math.BigInteger;

import  java.util.ArrayList;

import  java.util.Iterator;

import  java.util.*;

import  java.util.concurrent.atomic.AtomicLong;

/**

  *  

  *   @author   CoolPrince

  *   策略设计模式:

  *  

  */

public   class  StrategyModel {

public   static  <T> T reduce(Iterable<T> seq,Combiner<T> combiner){

Iterator<T> it = seq.iterator();

T result =  null ;

if (it.hasNext()){

result = it.next();

while (it.hasNext()){

result = combiner.combine(result, it.next());

}

}

return  result;

}

public   static  <T> Collector<T> forEach(Iterable<T> seq, Collector<T> func){

for (T t:seq){

func.function(t);

}

return  func;

}

public   static  <R,T> List<R> tansform(Iterable<T> seq,UnaryFunction<R, T> func){

List<R> result =  new  ArrayList<R>();

for (T t:seq){

result.add(func.function(t));

}

return  result;

}

public   static  <T> List<T> filter(Iterable<T> seq,UnaryPredicate<T> pred){

List<T> result =  new  ArrayList<T>();

for (T t:seq){

if (pred.test(t)){

result.add(t);

}

}

return  result;

}

static   class  IntegerAdder  implements  Combiner<Integer>{

public  Integer combine(Integer x, Integer y) {

return  x+y;

}

}

static   class  IntegerSubtracter  implements  Combiner<Integer>{

public  Integer combine(Integer x, Integer y) {

return  x-y;

}   

}

static   class  BigDecimalAdder  implements  Combiner<BigDecimal>{

public  BigDecimal combine(BigDecimal x, BigDecimal y) {

return  x.add(y);

}

}

static   class  BigIntegerAdder  implements  Combiner<BigInteger>{

public  BigInteger combine(BigInteger x, BigInteger y) {

return  x.add(y);

}

}

static   class  AtomicLongAdder  implements  Combiner<AtomicLong>{

public  AtomicLong combine(AtomicLong x, AtomicLong y) {

return   new  AtomicLong(x.addAndGet(y.get()));

}

}

static   class  BigDeimalUlp  implements  UnaryFunction<BigDecimal, BigDecimal>{

public  BigDecimal function(BigDecimal x) {

return  x.ulp();

}

}

static   class  Greaterthan<T  extends  Comparable<T>>  implements  UnaryPredicate<T>{

private  T  bound ;

public  Greaterthan(T bound){

this . bound  = bound;

}

public   boolean  test(T x) {

return  x.compareTo( bound ) > 0;

}

}

static   class  MultiplyingIntegerCollector  implements  Collector<Integer>{

private  Integer  val  = 1;

public  Integer function(Integer x){

val *= x;

return   val ;

}

public  Integer result() {

return   val ;

}

}

public   static   void  main(String[] args) {

          List<Integer>  list = Arrays. asList (1,2,3,46);

          Integer res =   reduce (list, new  IntegerAdder());

          System. out .println(res);

          

          res =  reduce (list,  new  IntegerSubtracter());

           print (res);

          

           print ( filter (list, new  Greaterthan<Integer>(4)));

          

           print ( forEach (list, new  MultiplyingIntegerCollector()).result());

           print ( forEach ( filter (list, new  Greaterthan<Integer>(4)), new  MultiplyingIntegerCollector()).result());

           print ( "" );

}

public   static   void  print(Object obj){

System. out .println(obj);

}

}

interface  Combiner<T> {T combine(T x,T y);}

interface  UnaryFunction<R,T>{ R function(T x);}

interface  Collector<T>  extends  UnaryFunction<T, T>{T result();}

interface  UnaryPredicate<T>{ boolean  test(T x);}

后话: Java 的泛型机制最吸引人的地方就是在使用容器类的地方,泛型的核心是重用和解决一些多态无法解决的问题,泛型的表达意识很强,所以又很好的可读性。泛型的内容比较丰富,最重要的是理解什么是泛型,为什么使用,有哪些缺陷,值得注意的原理是什么(比如擦除),有哪些设计模式在泛型中运用广泛,总之泛型看上去很糟糕但是在大型的运用中间还是比较广的小的运用中,一般情况用的多的也就是容器。总算总结完了,真的写这篇文章就花了整整 4 天的时间,确实泛型和并行是 Java 的两大难点,感觉学了泛型之后慢慢对以前写过的东西有所反思,很多地方都可以用泛型进行简化,从而减少了代码量。学习技术是个过程,现在慢慢有所体会学习技术是不可能一蹴而就的,也不能存在投机的心理,这样才能一步一个脚印。其实再写这篇文章的时候很想不在继续写下去了,但是后来一想,决定的事还是慢慢干下去,不然学的东永远只是皮毛。学习的进度只有计划的 1/3; 但是话有说回来,学技术不能光赶进度。有一句话说的好:什么是技术牛人,就是你的学习进度永远在别人的前面,别人学的是你已经学过的东西。是的,我的这篇文章很大一部分都是书上的一些基本的原理,其中也大量参杂了个人的一些见解和对程序的领悟。四天的时间来写这个论文我还是觉得没有浪费,尽管效率不是很高,如果谁看到这篇总结的文章能将其看完那么这个人也是一个能坚持住的人,不说别的泛型也能上升一个层次了。时候不早了,就此住笔。

 
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值