16.泛型介绍及泛型类、泛型方法、泛型接口

什么是泛型

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。

泛型有什么好处

在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");

没有泛型的时候是这样的

ArrayList al = new ArrayList();
al.add("ysjian001");
al.add(1);
al.add(new Object());

这段代码看似功能强大,为什么呢?因为它似乎能够往集合添加各种类型的对象(int类型会被装箱成Integer对象类型),貌似一些老程序员也倾向于这么去做,而且他们可以理直气壮的告诉我理由:我这么做想存什么就存什么!先不否定这种说法,让我们继续,看看下面代码:

String first = (String) al.get(0);

往集合里面存值就是为了后期取出来用的,而不是System.out.println(first),这里就产生了一个强制转换的问题,而往往这种类型的强制转换在编译器是允许通过的,而写程序的人们会犯下无意间的错误,错误的进行了强制转换,导致程序运行失败。

强制类型转换导致的程序运行失败的原因是没有在编译器对类型进行控制,看看code01调用ArrayList对象的add方法,任何类型都是可以添加进行的,编译器无法进行错误检验,埋下了安全隐患,例如:

ArrayList al = new ArrayList();
// 无法进行错误检查,File对象可以添加进去,编译器和运行期都可以通过
al.add(new File()); 
String first = (String) al.get(0);  // 类型转换失败导致运行失败

不用泛型的缺点

没有泛型的程序面临两个问题:

  1. 编译器无法进行类型检查,可以向集合中添加任意类型的对象。
  2. 取值时类型转换失败导致程序运行失败。

没有泛型的程序导致的后果:

  1. 程序的可读性有所降低,因为程序员可以不受限制往集合中添加任意对象。
  2. 程序的安全性遭到质疑,类型转换失败将导致程序运行失败。

Java5泛型提供了一个更好的解决方案:类型参数(type parameters),使用泛型的程序改善上述代码如下:

ArrayList<String> al = new ArrayList<String>();
al.add( "ysjian001");
// al.add(new Thread()); // 定义了String类型参数,添加File对象会报错
String first =  al.get(0);// 使用泛型后取值不用进行类型转换

到这里,通过前后对比,泛型的好处是不是很清楚了呢?为什么用泛型呢?
因为出现编译错误比类在运行时出现强制类型转换异常要好得多,泛型的好处在于提高了程序的可读性和安全性,这也是程序设计的宗旨之一。

什么时候使用泛型

使用泛型类是一件很轻松的事,集合框架中的类都是泛型类,用起来很方便。有人会想类型限制我们为什么不直接用数组呢?这个问题就好像问为什么集合优于数组,数组是固定的,而集合是可以自动扩展的。另外在实际中,实现一个泛型其实并不是那么容易。

大多数应用程序员对泛型的熟练程度仅仅停留在使用泛型上,像集合类中的List、Set和Map这些泛型集合用的很多,他们不必考虑这些泛型集合的工作方式和原理。那么当把不同的泛型类混合在一起使用时,或者对Java5之前的遗留代码进行衔接时,可能会看到含糊不清的的错误消息。这样一来,程序员就需要学习Java泛型来解决问题了,而不是在程序中胡乱猜测了。最终,部分程序员可能想要实现自己的泛型类和泛型方法。

提炼出泛型程序设计的三种熟练程度就是:

  1. 仅仅使用泛型。
  2. 学习泛型解决一些问题。
  3. 掌握泛型,实现自己的泛型。

怎么使用泛型

如何使用泛型听起来是一件很容易的事情,因为Sun公司的那些工程师已经做了很大努力,而需求总是会稍微苛刻一点的,需要解决因为缺乏类型参数模糊不清的问题,或者我们有必要实现自己的泛型来满足业务需求,所以学习和掌握泛型是很有必要的。最常见的自定义泛型的场景是泛型类、泛型方法、泛型接口。

泛型类

把泛型定义在类上
格式:public class 类名<泛型类型1,…>
注意:泛型类型必须是引用类型

早期的时候,我们使用Object来代表任意的类型。
向上转型是没有任何问题的,但是在向下转型的时候其实隐含了类型转换的问题。
也就是说这样的程序其实并不是安全的。所以Java在JDK5后引入了泛型,提高程序的安全性。
下面我们就来学习泛型类是怎么回事

// 泛型类:把泛型定义在类上 
public class ObjectTool<T> { 
      private T obj; 
      public T getObj() { 
         return obj; 
      } 
      public void setObj(T obj) { 
           this.obj = obj;
     }
}
//  泛型类的测试 
public class ObjectToolDemo { 
    public static void main(String[] args) { 
        ObjectTool<String> ot = new ObjectTool<String>();    
        ot.setObj(new String("中国")); 
        String s = ot.getObj(); 
        System.out.println("姓名是:" + s); 
        ObjectTool<Integer> ot2 = new ObjectTool<Integer>();    
        ot2.setObj(new Integer(69)); 
        Integer i = ot2.getObj(); 
        System.out.println("年龄是:" + i); 
   }
}

输出结果:
姓名是:中国
年龄是:69

泛型方法

把泛型定义在方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 .)

上面我们把泛型定义在了类中,现在我们也可以把泛型定义在方法中,来一起学习

* * 泛型方法:把泛型定义在方法上 **
public class ObjectTool {  
      public <T> void show(T t) {
           System.out.println(t); 
      }
}
  public class ObjectToolDemo { 
        public static void main(String[] args) { 
             // 定义泛型方法后
            ObjectTool ot = new ObjectTool(); 
            ot.show("hello"); 
            ot.show(100);
            ot.show(true);
       }
  }

这样我们就可以传递任意类型的参数了

泛型接口

把泛型定义在接口上
格式:public interface 接口名<泛型类型1…>

/* * 泛型接口:把泛型定义在接口上 */
public interface Inter<T> { 
  public abstract void show(T t);
}
//实现类在实现接口的时候,我们会遇到两种情况
//第一种情况:已经知道是什么类型的了
public class InterImpl implements Inter<String> { 
@Override 
public void show(String t) { 
  System.out.println(t);
}
}
//第二种情况:还不知道是什么类型的
public class InterImpl<T> implements Inter<T> { 
@Override 
public void show(T t) { 
       System.out.println(t);
}
}
public class InterDemo { 
  public static void main(String[] args) {
      // 第一种情况的测试
     Inter<String> i = new InterImpl(); 
     i.show("hello"); 
     // 第二种情况的测试
     Inter<String> i = new InterImpl<String>(); 
     i.show("hello"); 
     Inter<Integer> ii = new InterImpl<Integer>(); 
     ii.show(100);
}
}

我们来写一个简单的例子验证一下上面所说的结论

public class GenericDemo {
    public static void main(String[] args) {
        // 泛型如果明确的写的时候,前后必须一致 Collection<Object> c1 = new ArrayList<Object>();
        // Collection<Object> c2 = new ArrayList<Animal>();//报错
        // Collection<Object> c3 = new ArrayList<Dog>();//报错
        // Collection<Object> c4 = new ArrayList<Cat>();//报错

        // ?表示任意的类型都是可以的
        Collection<?> c5 = new ArrayList<Object>();
        Collection<?> c6 = new ArrayList<Animal>();
        Collection<?> c7 = new ArrayList<Dog>();
        Collection<?> c8 = new ArrayList<Cat>();

        // ? extends E:向下限定,E及其子类
        // Collection<? extends Animal> c9 = new ArrayList<Object>();//报错
        Collection<? extends Animal> c10 = new ArrayList<Animal>();
        Collection<? extends Animal> c11 = new ArrayList<Dog>();
        Collection<? extends Animal> c12 = new ArrayList<Cat>();

        // ? super E:向上限定,E极其父类
        Collection<? super Animal> c13 = new ArrayList<Object>();
        Collection<? super Animal> c14 = new ArrayList<Animal>();
        // Collection<? super Animal> c15 = new ArrayList<Dog>();//报错
        // Collection<? super Animal> c16 = new ArrayList<Cat>();//报错
    }
}

class Animal {
}

class Dog extends Animal {
}

class Cat extends Animal {
}

仔细观察一下上面的通配符有什么区别,你会很快的学会通配符的使用

泛型在实际开发中的应用场景

泛型在实际开发中用的很多,比如常见的Dao层的父类BaseDao,这里面一般是一个泛型类,把实体作为通配符传过去。

源码下载

本工程详细源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 1. 泛型概述 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,...
    JackChen1024阅读 501评论 0 3
  • object 变量可指向任何类的实例,这让你能够创建可对任何数据类型进程处理的类。然而,这种方法存在几个严重的问题...
    CarlDonitz阅读 898评论 0 5
  • 那时他们还小。 父亲常年随机械队外出揽活,母亲一下车间就是大半天,没人管他们兄妹。他身子弱,喜文静;小妹则相反,母...
    小陈皮阅读 176评论 0 3
  • 有一件事,我一直很内疚,至少是前半生。 虽然我的闺蜜这些年来一遍遍的跟我说,没关系不是你的错,即使你不那么做,最后...
    沧角阅读 1,466评论 0 2
  • 大数据时代的到来,令人类的竞争变得越来越激烈,不光成年人对自己的未来忧心忡忡,连儿童(包括没出生的)也被卷进了竞争...
    琢爱舟阅读 1,919评论 14 34