Java泛型使用示例整理

Java泛型使用示例整理

目标

Java泛型编程是JDK1.5版本后引入的。泛型让编程人员能够使用类型抽象,通常用于集合里面。本文旨在整理一些泛型的用法。

用法

泛型分两部分。一部分是泛型类和方法的定义。另一部分是泛型类和方法的使用。

定义篇

类定义时,使用泛型

在定义类的时候,我们可以使用泛型。如下代码:

class Demo <T> {
    T field;
    
    public void setFiled(T field) {
        this.field = field;
    }
    
    public T getField() {
        return field;
    }
}

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        Demo<String> demo = new Demo<String>();
        demo.setFiled("www.bo56.com");
        System.out.println(demo.getField());
    }
}

类名后面增加<T>,说明是泛型类。T可以视为类型的占位符。泛型类的代码就可以使用这个占位符T。

无参数方法定义时,使用泛型

无论在泛型类,还是普通类中,我们都可以再方法中使用泛型。

import java.util.ArrayList;
import java.util.List;

class Demo {
    
    public <T> List<T> newArrayList() {
        return new ArrayList<T>();
    }
}

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        Demo demo = new Demo();
        List<String> list = demo.newArrayList();
        list.add("www.bo56.com");
        list.add("bo56.com");
        //list.add(1); 报错。只能添加String
        for (String str:list) {
            System.out.println(str);
        }
    }
}

方法的返回值前面,修饰符后面增加<T>,表示为泛型方法。这样,就可以在方法的代码中使用T代表类型。
没有参数的泛型方法,类型的确定,是根据等号左边的类型推导泛型的最终类型。

有参数方法定义时,使用泛型

class Demo {
    
    public <T> void showClass(T t) {
        System.out.println(t.getClass());
    }
}

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        Demo demo = new Demo();
        demo.showClass("123");
        demo.showClass(123);
    }
}

有参数的泛型方法,类型的确定,是根据参数类型自动推导。

方法定义时,使用通配符 ?

import java.util.ArrayList;
import java.util.List;

class Demo {
    
    public void show(List<?> list) {
        list.add(null);
        //list.add(123); 编译错误
        for (Object object:list) {
            System.out.println(object);
        }
    }
}

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        Demo demo = new Demo();
        List<String> listStr = new ArrayList<String>();
        listStr.add("abc");
        demo.show(listStr);
        List<Long> listLong = new ArrayList<Long>();
        listLong.add(123L);
        demo.show(listLong);
    }
}

1、只能往集合中add null。
2、因为集合中的类型不确定。因此,为了安全,转换为Object。

类或者方法定义时,使用通配符 <T extends Number>

class Demo {
    
    public <T extends Number> void showClass(T t) {
        System.out.println(t.getClass());
    }
}

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        Demo demo = new Demo();
        demo.showClass(123);
        demo.showClass(123f);
        demo.showClass(123L);
        // demo.showClass("123"); 有错误 参数的类型,只能是 Number类型或者其子类
    }
}

<T extends Number> 表示传入的类型必须是Number或者其子类型。

使用篇

指定固定的类型

import java.util.ArrayList;
import java.util.List;

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        List<Long> list = new ArrayList<Long>();
        //List<Number> listN = new ArrayList<Long>(); 编译错误。List<Number> 并不是 ArrayList<Long> 的父类。
    }
}

如果变量声明时,为泛型指定的类型为固定类型。如List<Long>,就是为泛型指定的类型为Long。那么后续在给变量赋值时,指定的变量也得为这个类型。如 ArrayList<Long>(),指定的也是Long。
对于泛型来说,Long是Number的子类。但是,List<Long>并不是List<Number>的子类。

使用通配符 ?

import java.util.ArrayList;
import java.util.List;

class Food {}

class Fruit extends Food {}

class Apple extends Fruit {}

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        List<?> list = new ArrayList<Fruit>();
        //list.add(new Food()); 编译错误
        //list.add(new Fruit()); 编译错误
        //list.add(new Apple()); 编译错误
        list.add(null);
        //Food food = list.get(0);
        //Fruit fruit = list.get(0);
        //Apple apple = list.get(0);
        Object object = list.get(0);
    }
}

1、只能添加null。
2、获取的值只能赋值给Object类型。
因为通配符?表示该集合存储的元素类型未知,可以是任何类型。往集合中加入元素需要是一个未知元素类型的子类型,正因为该集合存储的元素类型未知,所以我们没法向该集合中添加任何元素。唯一的例外是null,因为null是所有类型的子类型,所以尽管元素类型不知道,但是null一定是它的子类型。

使用上界通配符 <? extends Fruit>

import java.util.ArrayList;
import java.util.List;

class Food {}

class Fruit extends Food {}

class Apple extends Fruit {}

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        List<? extends Fruit> list = new ArrayList<Fruit>();
        // List<? extends Fruit> listA = new ArrayList<Food>(); 编译错误。不能为父类。
        List<? extends Fruit> listN = new ArrayList<Apple>();
        listN.add(null);
        //listN.add(123); 不能add
        Fruit fruit = listN.get(0);
        Food food = listN.get(0);
        //Apple apple = listN.get(0); 编译错误。get获取的值,只能给父类
        listN.remove(0);
    }
}

上界通配符,一般用于读取的场景。
1、为泛型指定的类型只能是Fruit类型或者其子类。
2、只能为其列表添加null。
3、get方法获取的值只能赋值给Fruit类或者其超类。

使用下界通配符 <? super Fruit>

import java.util.ArrayList;
import java.util.List;

class Food {}

class Fruit extends Food {}

class Apple extends Fruit {}

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        List<? super Fruit> list = new ArrayList<Fruit>();
        List<? super Fruit> listA = new ArrayList<Food>(); 
        //List<? super Fruit> listN = new ArrayList<Apple>(); 编译错误,不能为子类
        listA.add(new Fruit());
        //listA.add(new Food()); 编译错误,不能为父类。
        listA.add(new Apple());
        Object object = listA.get(0);
        //Fruit fruit = listA.get(0);编译错误。
        //Food food = listA.get(0);编译错误。
        //Apple apple = listA.get(0); 编译错误。
    }
}

下界通配符,一般用于写入的场景。
1、为泛型指定的类型必须为Fruit,或者其超类。
2、可以为其列表添加任意Fruit类型,或者其子类。
3、get方法获取的类型,只能赋值给Object类型。

边界通配符总结

边界通配符总结
如果你想从一个数据类型里获取数据,使用 ? extends 通配符
如果你想把对象写入一个数据结构里,使用 ? super 通配符
如果你既想存,又想取,那就别用通配符。

注意

泛型类型是被所有调用共享的

import java.util.ArrayList;
import java.util.List;

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        List<String>l1 = new ArrayList<String>();  
        List<Integer>l2 = new ArrayList<Integer>();  
        System.out.println(l1.getClass() == l2.getClass()); //True  
    }
}

所有泛型类的实例都共享同一个运行时类,类型参数信息会在编译时被擦除。因此考虑如下代码,虽然ArrayList<String>和ArrayList<Integer>类型参数不同,但是他们都共享ArrayList类,所以结果会是true。

instanceof

import java.util.ArrayList;
import java.util.Collection;

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        Collection cs = new ArrayList<String>();  
        if (cs instanceof Collection<String>){}// compile error.如果改成instanceof Collection<?>则不会出错。  
    }
}

不能对确切的泛型类型使用instanceOf操作。如下面的操作是非法的,编译时会出错。

泛型数组问题

import java.util.ArrayList;
import java.util.List;

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        List<String>[] lsa = new ArrayList<String>[10]; //compile error.
    }
}

不能创建一个确切泛型类型的数组。


import java.util.ArrayList;
import java.util.List;

public class Test {
    
    public static void main (String[] args) throws java.lang.Exception
    {
        List<?>[] lsa = new ArrayList<?>[10]; // ok, array of unbounded wildcard type  
    }
}

能创建带通配符的泛型数组.

参考

http://qiemengdao.iteye.com/blog/1525624
http://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • 前言 泛型(Generics)的型变是Java中比较难以理解和使用的部分,“神秘”的通配符,让我看了几遍《Java...
    珞泽珈群阅读 7,761评论 12 51
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,862评论 6 13
  • 开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误。比如一个方法如果接收List作为形式参数,那么如果尝试...
    时待吾阅读 1,040评论 0 3
  • 人倒着走 车子倒着开 时光倒着奔跑 你倒着回到她的身边 公司两个月前新来了一个运营总监,30岁出头,超严肃的范。身...
    方不见阅读 1,041评论 4 23
  • 【1】忠诚 单位不一定挽留有能力的员工,但对一个忠心耿耿的人,不会有领导愿意让他走,他会成为单位这个铁打营盘中最长...
    立信会计阅读 432评论 0 0