黑马程序员技术交流社区

标题: Java中的泛型 [打印本页]

作者: 鱼丸儿    时间: 2017-12-20 14:16
标题: Java中的泛型
本帖最后由 鱼丸儿 于 2017-12-21 19:47 编辑

  泛型在Java中有很重要的地位,网上很多文章罗列各种理论,不便于理解,并且很多都讲的不够完整,尤其是关于泛型上限和泛型下限的问题。本篇将立足于代码介绍、总结了关于泛型的知识。希望能给你带来一些帮助。
     首先我们先介绍下泛型的基本格式。
     泛型格式:
            通过<>来定义要操作的引用数据类型。
     接下去我们来说下泛型可以定义在哪些地方。
            1.泛型类(泛型定义在类上)
                   当某个类中有有些数据类型不确定,需要在使用该类的时候来确定的话。这个时候我们可以把泛型定义在类上。最常见泛型类的就是集合。代码如下:
[Java] 纯文本查看 复制代码
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
     //省略其它代码
}

              以上是ArrayList的源码,我们可以发现在类名的后面加了个<E>。这就是泛型类的定义格式。然后我们来学着写一个属于自己的泛型类。代码如下。
[AppleScript] 纯文本查看 复制代码
public class Box<E> {

}

             这样我们就定义了一个泛型类Box。但是我们现在发现这个泛型定义的好像没什么用。那我们就先看看ArrayList中定义的泛型是有什么用处的。我们看ArrayList的源码可以发现如下几个常用方法的返回值类型都是E,并且有些方法的参数类型也是用来表示的。
            
             通过这个我们就可以发现原来这里的E就可以用来表示某种数据类型了。所以我们也可以在我们自己的泛型类里面去使用泛型E。
[Java] 纯文本查看 复制代码
public class Box<E> {
        E element;
        public void setElement(E e){
                element=e;
        }
        public E getElement(){
                return element;
        }
}

            在上面的代码中我们就在泛型类中多次使用了E。但是E具体是指什么类型呢?这就要看我们使用该类的时候写的具体是什么类型了。接下来我们就来使用下Box这个类。
           
            我们发现如果我们的泛型写成了String只会,所有写E的地方类型都变成String。
            这就是泛型类。如果我们在某个类中需要使用到一种类型,但是这种类型还不确定。需要在使用的时候才能确定。就可以使用泛型来实现这种需求。
             2.泛型方法(泛型定义在方法上)
           如果我们在类上定义了泛型E,这个时候某个方法需要使用另外一个泛型T,这里就会报错,因为我们只定义了E。所以这个时候我们一般有两个解决方法,一个是在类上再加上一个泛型T,另外一个方法是在方法上在定义一个泛型T。定义的代码如果下。
         
[Java] 纯文本查看 复制代码
public <T> T method(T t){
                return t;
        }
          那方法上的泛型T是什么时候确定类型的呢?
          我们发现调用该方法的时候并不需要在方法调用的地方显式的声明T的数据类型。只需要在该方法的参数传入一个String类型的参数,最后T就表示String类型,所以最后的返回值也变为了String类型。传一个一个整数的话返回值就变成了Integer类型。代码如下。
         
[Java] 纯文本查看 复制代码
public static void main(String[] args) {
                // TODO Auto-generated method stub
                Box<String> box=new Box<>();
            String method = box.method("a");
            Integer method2 = box.method(1);
            
        }
                 3.泛型限定

       我们在写方法的时候,有的时候我们会把参数的类型写成某个类型的父类,这样写的好处是我在调用这个方法的时候那个父类的其他子类也可以做为参数传递进来。举个栗子:
       方法定义如下
   
[Java] 纯文本查看 复制代码
        public void method2(Person p){
               
        }
        然后我们来调用下方法
      
[Java] 纯文本查看 复制代码
            box.method2(new Person());
            box.method2(new Student());
       这个时候我们可以发现,我们在调用这个方法的时候即可以传Person对象,也可以传Student对象。
      那么再来写一个方法。
   
[Java] 纯文本查看 复制代码
        public void setBox(Box<E> box){
                element=box.element;
        }
      其实这个方法就是把传进来的box对象的element赋值给我这个对象的成员变量element。这个时候我们去调用一下该方法。
      
      这个时候我们会发现我如果传一个泛型为Person类型的Box对象没有问题。但是我如果传了一个泛型为Student类型的Box对象就报错了。在我们的Box这个类中,泛型表示的其实就是Element的数据类型。我们的box2对象泛型是设置为Person的。所以box2这个对象element是Person类型的。而Person类型的变量是可以接收一个Student类型的对象的。所以其实我们是可以把一个泛型为Student的Box对象里面的element赋值给泛型为 Person的Box对象里面的element的。但是如果我们在该方法的参数上,数据类型写成了Box<E>类型的话,我们在调用的时候发现该方法只能传一个泛型和box2泛型完全一样的Box对象。那我们的方法的参数类型应该怎么写才可以让方法既能接收泛型为Person的Box对象,又让方法能接收泛型为Person的子类的Box对象呢?这就要使用泛型限定了。
     
[Java] 纯文本查看 复制代码
        public void setBox(Box<? extends E> box){
                element=box.element;
        }
     <?  extends E>这个泛型表示,该泛型的类型可以是所有继承了E这个泛型的子类,包括E。
    这个时候我们在来调用下该方法我们会发现该方法的确可以接收泛型为Student类型的Box对象了。
   其实这种泛型限定叫做泛型上限。我们还有一种限定叫泛型下限,格式如下:   ? super    E  这就表示该泛型的类型是E的父类,包括E本身。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2