Java Review - 泛型

简介

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

  1. 常用的泛型例子
  2. 为什么使用泛型
  3. 泛型的使用
  4. 泛型的通配符&上限下限
  5. 泛型的特性
  6. 泛型数组

一: 常用的泛型例子

1.1 集合的泛型规范

public static void main(String[] args) {
    List<String> names = new ArrayList<String>();
    names.add("jack");
    names.add("rose");
}

我们指定集合names为String类型的集合,因此在编译期间,此集合只能添加String类型的数据,做到集合的规范作用

1.2 类范围规范

class Meat { }
class Hamburger extends Meat { }
class MacHamburger extends Hamburger{}

public void main() {
    Info<? extends Meat> temp0 = new Info<>(new MacHamburger());//上限
    Info<Meat> temp1 = new Info<>(new Meat());
    Info<Hamburger> temp2 = new Info<>(new Hamburger());
    Info<MacHamburger> temp4 = new Info<>(new MacHamburger());
    Info<? super MacHamburger> temp5 = new Info<>(new Meat());//下限
}

class Info<T> {
    private T var;// 定义泛型变量

    Info(T var){ this.var=var; }

    public void setVar(T var) { this.var = var; }

    public T getVar() { return this.var; }

    public String toString() { return this.var.toString(); }// 直接打印
}

通过泛型来控制Info类可以指定的食物,不像以前简单的指定一种类型,我们可以通过?通配符和extends 上限super 下限来实现一定范围的类规范。比如:<? extends Meat>只能传入Meat类及其子类,或比如:<? super MacHamburger>只能传入MacHamburger类及其父类。

二:为什么使用泛型

从上面的集合例子来讲,在Java SE 1.5之前 是没有这个特性的。那如果需要同时有string规范的集合,又有int规范的集合等,岂不是要有很多个新的List类来进行匹配,这样子的话类的数量会增加很多,而且不好做统一维护。

三:泛型的使用

1.1 泛型类

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{ 
    //key这个成员变量的类型为T,T的类型由外部指定  
    private T key;

    public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
        this.key = key;
    }

    public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
        return key;
    }
}

泛型的类型参数只能是类类型Interger,不能是简单类型int

泛型如果不传入泛型实参的话,就不会起到限制作用,泛型类的泛型方法和成员变量定义的类型可以为任何类型

1.2 泛型接口
用法基本与泛型类相同

//定义一个泛型接口
public interface Generator<T> {
    public T next();
}

实现泛型接口的类,未传入泛型实参,此泛型类与普通泛型类定义相同,需要将泛型的声明一起加入类中

class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}

实现泛型接口的类,传入泛型实参,则所有使用泛型的地方都要替换成传入的实参类型

public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

1.3 泛型方法

泛型类 是在实例化的时候指明泛型的具体类型
泛型方法 是在调用方法的时候指明泛型的具体类型

泛型方法能使方法独立于类的产生变化

只有声明了<T>的方法才是泛型方法,泛型类中使用了泛型的成员方法并不是泛型方法

//泛型类
class Test<T> {
    //成员方法
    public void funA(T t){}
    //泛型方法1
    public <K> void funB(K k){}
    //泛型方法2 此T是方法泛型T 不是类泛型T
    public <T> void funC(T t){}
    //泛型方法3 泛型方法不仅可以使用自身泛型,也可以用类泛型
    public <K> void funD(K k,T t){}
}

可变参数

public <T> void printMsg( T... args){
    for(T t : args){
        Log.d("泛型测试","t is " + t);
    }
}

printMsg("111",222,"aaaa","2323.4",55.55);

静态方法无法使用类泛型,静态方法若要使用泛型,必须让其成为泛型方法,尽管如此,依然无法使用类泛型

public class StaticGenerator<T> {

    //编译错误
    public static  void show(T t){}

    public static <T> void show(T t){}
}

四:泛型的通配符&上限下限

1.1 通配符
Integer是Number的子类,List<Integer>和List<Number>实际上也是同一种基本类型。但是Generic<Number>作为形参,能否使用Generic<Integer>的实例传入呢?

public void showKeyValue1(Generic<Number> obj){}

Generic<Integer> gInteger = new Generic<Integer>(123);
Generic<Number> gNumber = new Generic<Number>(456);

showKeyValue(gInteger);//编译错误
showKeyValue(gNumber);

同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的

那如何解决上面的问题,达到同时表示Generic<Integer>和Generic<Number>父类的引用类型呢?类型通配符为此而来

public void showKeyValue1(Generic<?> obj){}

类型通配符一般用?表示,代替具体的类型实参,如果单写?可以简单理解是Object作为实参。

1.2 泛型上限下限
泛型的上下线顾名思义就是限制泛型的使用范围。
extends为上限
super为下限

//通配符 只能使用Number本身及其Number的子类的泛型
public void showKeyValue1(Generic<? extends Number> obj){}
//泛型类
public class Generic<T extends Number>{
    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}


Generic<String> a = new Generic<String>("11111");//错误
//泛型方法的边界必须在泛型声明的时候添加
public <T extends Number> T showKeyName(Generic<T> fn){
    T test = fn.getKey();
    return test;
}

五:泛型的特性

泛型仅在编译阶段是有效

例子:

List<String> list1 = new ArrayList<String>();
List<Integer> list2 = new ArrayList<Integer>();

Class class1 = list1.getClass();
Class class2 = list2.getClass();

if(class1.equals(class2)){
    Log.d("pmm","类型相同");
}

在编译期间,正确检查泛型后,编译器会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处天价类型检查和类型转换的方法。即泛型信息不会进入到运行阶段

泛型类型在逻辑上看似是多个不同的类型,实则相同

六:泛型数组

在java中 不能创建一个确切的泛型类型的数组

List<String>[] ls = new ArrayList<String>[10];//错误
List<?>[] ls = new ArrayList<?>[10];//可以
List<String>[] ls = new ArrayList[10];//可以

例子1:

List<String>[] lsa = new List<String>[10]; // Not really allowed.    
Object o = lsa;    
Object[] oa = (Object[]) o;    
List<Integer> li = new ArrayList<Integer>();    
li.add(new Integer(3));    
oa[1] = li; // Unsound, but passes run time store check    
String s = lsa[1].get(0); // Run-time error: ClassCastException.

这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList而不会出现异常,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。

而对泛型数组的声明进行限制,对于这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。

例子2:

List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.    
Object o = lsa;    
Object[] oa = (Object[]) o;    
List<Integer> li = new ArrayList<Integer>();    
li.add(new Integer(3));    
oa[1] = li; // Correct.    
Integer i = (Integer) lsa[1].get(0); // OK 

上面采用通配符的方式是被允许的:数组的类型不可以是类型变量,除非是采用通配符的方式,因为对于通配符的方式,最后取出数据是要做显式的类型转换的

PS:本文整理自以下博客
java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
Java总结篇系列:Java泛型
若有发现问题请致邮 caoyanglee92@gmail.com

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

推荐阅读更多精彩内容