java-泛型详解

1、概述,什么是泛型

  • 泛型,即参数化类型
  • 将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)

泛型的特性

  • 编译时有效,编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。

泛型的使用

  • 泛型类
  • 泛型接口
  • 泛型方法
泛型类

最典型的有各种容器类,如:List、Map、Set

写法

class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
  private 泛型标识 /*(成员变量类型)*/ var; 
  .....

  }
}

示例

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

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

    //getAge的返回值类型为T,T的类型由外部指定
    //2、注意:此方法中虽然使用了泛型,但是它并不是一个泛型方法。
    //这只是类中的一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型,
    //所以在这个方法中才可以继续使用T这个泛型
    public T getAge() {
        return age;
    }
}

调用

Person<Integer> xiaowang = new Person<>(10);
Person<String> xiaolu = new Person<>("20");
System.out.println("小刘年龄为:" + xiaolu.getAge() + "\n小王年龄为:" +  xiaowang.getAge().toString());

//定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,
//此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
Person xiaoli = new Person(30);
System.out.println("小刘年龄为:" + xiaolu.getAge() + "\n小王年龄为:" + xiaowang.getAge().toString() + "\n小李年龄为:" + xiaoli.getAge());

输出

小刘年龄为:20
小王年龄为:10
小刘年龄为:20
小王年龄为:10
小李年龄为:30

泛型接口

写法

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

示例

/** 未传入泛型参数时,与泛型类的定义相同,在声明的时候,需要将泛型的声明也一起加到类里
 * 即:class Teacher<T> implements IPerson<T>
 * 如果不声明泛型,如:class Teacher implements IPerson<T>,编译器会报错:"Unknown class"
 * */
public class Teacher<T> implements IPerson<T>{
    @Override
    public T next() {
        return null;
    }
}

/**
 * 传入泛型实参时:
 * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口IPerson<T>
 * 但是我们可以为T传入无数个实参,形成无数种类型的IPerson接口。
 * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
 * 即:Person<T>,public T next();中的的T都要替换成传入的String类型。
 */
public class MathTeacher implements IPerson<String> {
    @Override
    public String next() {
        return null;
    }
}

调用

Teacher<String> teacherWang = new Teacher<>();
teacherWang.next();

MathTeacher mathTeacherLi = new MathTeacher();
mathTeacherLi.next();

泛型通配符

示例

//类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。
// 重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !
// 再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型
public static void eat(Person<?> person){
        person.getAge();
}

调用

Person<Integer> xiaowang = new Person<>(10);
Person<String> xiaolu = new Person<>("20");
eat(xiaolu);

泛型方法

基本示例

//泛型方法: <T>表示泛型方法、<T>后面的T表示返回值,如果没有则是void
/**
 * @param tClass 传入的泛型实参
 * @return T 返回值为T类型
 * 1) public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
 * 2) 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法
 * 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T
 * 4)与泛型类的定义一样,此处T可以随便写为任意标识,如T、E、K、V等形式的参数常用于表示泛型
 * 5) <T>可以有多个泛型参数如<T,K,V>
 * */
public static <T,K,V> T genericTeacher(Class<T> tClass,Person<K> name) throws Exception{
    T instance = tClass.newInstance();
    Person<K> person = name;
    return instance;
}

泛型方法的基本用法

public class GenericTest {
    //这个类是个泛型类,在上面已经介绍过
    public class Generic<T> {
        private T key;

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

        //我想说的其实是这个,虽然在方法中使用了泛型,但是这并不是一个泛型方法。
        //这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
        //所以在这个方法中才可以继续使用 T 这个泛型。
        public T getKey() {
                return key;
        }

        /**
         * 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"
         * 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。
         public E setKey(E key){
         this.key = keu
         }
         */
    }

    /**
     * 这才是一个真正的泛型方法。
     * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
     * 这个T可以出现在这个泛型方法的任意位置.
     * 泛型的数量也可以为任意多个
     * 如:public <T,K> K showKeyName(Generic<T> container){
     * ...
     * }
     */
    public <T> T showKeyName(Generic<T> container) {
        System.out.println("container key :" + container.getKey());
        //当然这个例子举的不太合适,只是为了说明泛型方法的特性。
        T test = container.getKey();
        return test;
    }

    //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
    public void showKeyValue1(Generic<Number> obj) {
        System.out.println("泛型测试key value is " + obj.getKey());
    }

    //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
    //同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
    public void showKeyValue2(Generic<?> obj) {
        System.out.println("泛型测试key value is " + obj.getKey());
    }

    /**
     * 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
     * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
     * 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
     public <T> T showKeyName(Generic<E> container){
     ...
     }
     */

    /**
     * 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
     * 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
     * 所以这也不是一个正确的泛型方法声明。
     * public void showkey(T genericObj){
     * <p>
     * }
     */
}

类中的泛型方法

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

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

    //getAge的返回值类型为T,T的类型由外部指定
    //2、注意:此方法中虽然使用了泛型,但是它并不是一个泛型方法。
    //这只是类中的一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型,
    //所以在这个方法中才可以继续使用T这个泛型
    public T getAge() {
        return age;
    }
}

泛型方法与可变参数

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

静态方法与泛型

静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。

即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。

/**
 * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
 * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
 * 如:public static void show(T t){..},此时编译器会提示错误信息:
 "StaticGenerator cannot be refrenced from static context"
 */
public static <T> void show(T t){

}

泛型的上下界

在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。

示例

//泛型的上界
//为泛型添加上边界,即传入的类型实参必须是指定类型的子类型
public static void showKeyValue1(Person<? extends Number> obj){
        System.out.println("泛型测试key value is " + obj.getAge());
}

//泛型的下界
//为泛型添加下边界,即传入的类型实参必须是指定类型的子类型
public static void showKeyValue2(Person<? super Integer> obj){
        System.out.println("泛型测试key value is " + obj.getAge());
}

调用

showKeyValue1(new Person<>(1.0));
showKeyValue1(new Person<>(99));

showKeyValue2(new Person<>(100));
showKeyValue2(new Person<Number>(1.0));
showKeyValue2(new Person<Serializable>(1.0));

泛型数组

看这个文章吧

文章参考

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

推荐阅读更多精彩内容

  • 转自:https://blog.csdn.net/s10461/article/details/53941091 ...
    mayiwoaini阅读 239评论 0 0
  • java泛型详解 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。 什...
    莫夏_b560阅读 224评论 0 1
  • 最近项目组在进行泛型代码编写时遇到很多困难,讨论下来发现大家对这个概念都是一知片解,然而在我们的项目开发过程中,又...
    Caprin阅读 4,903评论 2 47
  • 第三十五章 偷得一日闲,山中寻幽静 今天己经是6月2日了,昨天把孩子的准考证打印出来了,考点是青岛二中,是...
    韬乐阅读 619评论 0 4
  • 最早听这句话还是十几岁的时候,那时候的我青春无知。那时候的我看待这句话总是在想再归来和我们如何还能是少年...
    木蔷阅读 442评论 0 0