Java泛型学习一

Java泛型

  所谓泛型,就是变量类型的参数化。泛型是java1.5中引入的一个重要特征,通过引入泛型,可以使编译时类型安全,运行时更少抛出ClassCastException的可能。一提到参数化,最熟悉的就是定义方法是由形参,然后调用此方法时传递实参。那么参数化该怎么理解,顾名思义,就是将类型由原来的具体类型参数化,类似于方法中的变量参数,此是类型也可定义成参数形式,然后在使用,调用时传入具体类型。使用泛型是如果不提供参数类型,即泛型类没有参数化,系统会警告,此时类型为Object。

为什么使用泛型

  首先看一段代码:

1 List list=new ArrayList();
2 list.add("asd");
3 list.add("qwe");
4 list.add(123);
5 for(int i=0;i<list.size();i++){
6     String name=(String) list.get(i);//1
7     System.out.println("name: "+name);
8 }

  在循环当中,由于忘记了之前的list中加入了Integer类型的值或者是其他原因,很容易出现类似于//1中的错误,因为在编译阶段正常,但是在运行时会出现ClassCastException异常。当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,该对象的编译类型变成了Object类型,但其运行时类型仍然为其本身类型,因此//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现转化异常。

  泛型可以限制加入集合当中的元素类型,是编译时不出现问题,运行时就不会抛出ClassCastException异常。使用泛型的典型例子,是在集合中的泛型使用。

  在使用泛型前,存入集合中的元素可以是任何类型的,当从集合中取出时,所有的元素都是Object类型,需要进行向下的强制类型转换,转换到特定的类型

  比如:

List myIntList = new LinkedList(); // 1

myIntList.add(new Integer(0)); // 2

Integer x = (Integer) myIntList.iterator().next(); // 3   

  第三行的这个强制类型转换可能会引起运行时的错误。

  泛型的思想就是由程序员指定类型,这样集合就只能容纳该类型的元素。

  使用泛型:

List<Integer> myIntList = new LinkedList<Integer>(); // 1'

myIntList.add(new Integer(0)); // 2'

Integer x = myIntList.iterator().next(); // 3'

  将第三行的强制类型转换变为了第一行的List类型说明,编译器会为我们检查类型的正确性。这样,代码的可读性和健壮性也会增强。

自定义泛型类,泛型接口,泛型方法

  先简单定义一个类: 

 1 public class GenericTest {
 2 
 3     public static void main(String[] args) {
 4 
 5         Box<String> name = new Box<String>("corn");
 6         System.out.println("name:" + name.getData());
 7     }
 8 
 9 }
10 
11 class Box<T> {
12 
13     private T data;
14 
15     public Box() {
16 
17     }
18 
19     public Box(T data) {
20         this.data = data;
21     }
22 
23     public T getData() {
24         return data;
25     }
26 
27 } 
复制代码

在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E、K、V等形式的参数常用于表示泛型形参,由于接收来自外部使用时候传入的类型实参。那么对于不同传入的类型实参,生成的相应对象实例的类型是不是一样的呢?

复制代码
 1 public class GenericTest {
 2 
 3     public static void main(String[] args) {
 4 
 5         Box<String> name = new Box<String>("corn");
 6         Box<Integer> age = new Box<Integer>(712);
 7 
 8         System.out.println("name class:" + name.getClass());      // com.qqyumidi.Box
 9         System.out.println("age class:" + age.getClass());        // com.qqyumidi.Box
10         System.out.println(name.getClass() == age.getClass());    // true
11 
12     }
13 
14 }
复制代码

 

由此,我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本实例中为Box),当然,在逻辑上我们可以理解成多个不同的泛型类型。

究其原因,在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

泛型和子类:

List<String> ls = new ArrayList<String>(); // 1

List<Object> lo = ls; // 2

 一个String类型的List是一个Object类的List吗?

 不可以,Java编译器将会在第二行产生一个编译错误,因为它们的类型不匹配。

 这样就避免了如果lo引入加入Object类型的对象,而ls引用试图将其转换为String类型而引发错误。所以编译器阻止了这种可能。

继承泛型类别:

父类:

复制代码
public class Parent<T1,T2>
{
    private T1 foo1;
    private T2 foo2;
    
    public T1 getFoo1()
    {
        return foo1;
    }
    public void setFoo1(T1 foo1)
    {
        this.foo1 = foo1;
    }
    public T2 getFoo2()
    {
        return foo2;
    }
    public void setFoo2(T2 foo2)
    {
        this.foo2 = foo2;
    }    

}
复制代码

 子类继承父类:

复制代码
public class Child<T1, T2, T3> extends Parent<T1, T2>
{
    private T3 foo3;

    public T3 getFoo3()
    {
        return foo3;
    }

    public void setFoo3(T3 foo3)
    {
        this.foo3 = foo3;
    }
    
}

 

 

实现泛型接口:

泛型接口:

复制代码
public interface ParentInterface<T1,T2>
{
    public void setFoo1(T1 foo1);
    public void setFoo2(T2 foo2);
    public T1 getFoo1();
    public T2 getFoo2();

}
复制代码

  子类实现泛型接口:

复制代码
public class ChildClass<T1,T2> implements ParentInterface<T1, T2>
{
    private T1 foo1;
    private T2 foo2;
    
    @Override
    public void setFoo1(T1 foo1)
    {
        this.foo1 = foo1;
        
    }
    @Override
    public void setFoo2(T2 foo2)
    {
        this.foo2 = foo2;
    }
    @Override
    public T1 getFoo1()
    {
        return this.foo1;
    }
    @Override
    public T2 getFoo2()
    {
        return this.foo2;
    }

}
复制代码

 

调用泛型方法语法格式如下:

 

       定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。

       Class<T>的作用就是指明泛型的具体类型,而Class<T>类型的变量c,可以用来创建泛型类的对象。

       为什么要用变量c来创建对象呢?既然是泛型方法,就代表着我们不知道具体的类型是什么,也不知道构造方法如何,因此没有办法去new一个对象,但可以利用变量c的newInstance方法去创建对象,也就是利用反射创建对象。

       泛型方法要求的参数是Class<T>类型,而Class.forName()方法的返回值也是Class<T>,因此可以用Class.forName()作为参数。其中,forName()方法中的参数是何种类型,返回的Class<T>就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,因此返回的是Class<User>类型的对象,因此调用泛型方法时,变量c的类型就是Class<User>,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。

       当然,泛型方法不是仅仅可以有一个参数Class<T>,可以根据需要添加其他参数。

       为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。

泛型的继承规则:

 

设计两个类,它们之间有继承关系:

 

public class B {
    public void fun(){
        System.out.println("A.fun()");
    }
}

 

public class BSub extends B{
    @Override
    public void fun(){
        System.out.println("BSub.fun()");
    }


    public static void main(String[] args) {
        Pair<BSub> p1 = new Pair<BSub>();
       
        Pair p2 = p1;
        p2.setFirst(new Date());
        BSub b = p1.getFirst();
        b.fun();
    }
}

 

但其实Pair<B>和Pair<BSub>之间没有什么联系,绝对没有继承的关系。这种限制的原因是:

 

Pair<BSub> bsp = new Pair<BSub>();

 

Pair<B> bp = bsp;//不合法,错误:类型不兼容,但现在我们假设可以这样做

 

bp.setFirst(new B());

 

最后一句合法,但是bp与bsp是引用的同一个对象,那么可以将B对象装入Pair<BSub>中看起来也是不和情理的,退一步假设即使可以这样做,也会引发类型转换的错误,因为B是超类不能转换成子类。所以不如禁止这样做。

 

但是下面的代码是合法的:

 

        Pair<BSub> p1 = new Pair<BSub>();
//        Pair<B> p = p1;//不合法,类型不兼容

        Pair p2 = p1;
        p2.setFirst(new Date());
        BSub b = p1.getFirst();
        b.fun();
       
        Pair<B> p3 = new Pair<B>();
//        Pair<BSub> p4 = p3;//不合法,同样类型不兼容

 

但是会产生ClassCastException,把“谎话”说圆还是有难度的,这是Java对泛型实现的一个代价吧。

 

最后,泛型类可以拓展或实现其它的泛型类。就这一点而言,与普通的类没有什么区别。例如,ArrayList<T>类实现List<T>接口,这意味着一个ArrayList<BSub>可以被转换为一个List<BSub>。但是,正像前面所讨论的,一个ArrayList<BSub>不是一个ArrayList<B>或者List<B>。

 

下图显示了它们之间的关系:

 

Java泛型4---泛型类型的继承规则

 

参考资料:

http://www.cnblogs.com/mengdd/archive/2013/01/21/2869778.html

http://www.cnblogs.com/lwbqqyumidi/p/3837629.html

http://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html

http://www.cnblogs.com/sunwei2012/archive/2010/10/08/1845938.html

http://www.cnblogs.com/Fskjb/archive/2009/08/23/1552506.html

http://www.cnblogs.com/yinhaiming/articles/1749738.html

http://www.cnblogs.com/nerxious/archive/2012/12/21/2828121.html

http://www.cnblogs.com/anrainie/archive/2012/03/09/2387177.html

http://blog.sina.com.cn/s/blog_44c1e6da0100cus8.html

posted @ 2016-07-07 18:59  温暖的向阳花  阅读(1608)  评论(0编辑  收藏  举报