泛型是什么?它在java中又有哪些必要性?

Posted 守夜人爱吃兔子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了泛型是什么?它在java中又有哪些必要性?相关的知识,希望对你有一定的参考价值。

1、什么是泛型?

对于大部分同学而言,泛型一词并不陌生,因为在Java开发中经常会用。常用的List、Map都用到过,所以泛型在Java中的重要的地位不言而喻。同时泛型也被广泛应用于面向对象编程和各种设计模式中。那么究竟什么是泛型,我们又为什么要用泛型?

下面我们通过一道测试题来为同学们做解释:

List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();

System.out.println(l1.getClass() == l2.getClass());

上面输出的结果是什么?懂泛型的同学肯定知道答案,就是true,因为这是典型地泛型类型擦除。

泛型是Java5才有的。在此之前,Java中的对象集合,往往是需要转换为Object对象,取出对象也是要再次进行强制类型转换,这样的操作带来了ClassCastException风险。泛型的加入,使得集合在编译阶段就可以推断出集合中元素类型,减少了代码臃肿和异常风险。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

2、泛型的使用

定义类、接口或方法时使用类型形参(泛型),在声明变量、创建对象、调用方法时候会动态指定这个泛型。

2.1 泛型类

public class Apple<T> {
    //使用T类型定义实例变量
    private T info;
    public Apple(){}
    //使用T类型来定义构造器
    public Apple(T info){
        this.info = info;
    }
    public T getInfo() {
        return info;
    }
    public void setInfo(T info) {
        this.info = info;
    }
}
//使用
Apple<String> name = new Apple<>("苹果");
Apple<Double> price = new Apple<>(.);

泛型类与普通类的区别就是在方法名后使用进行了声明泛型形参,接下来可以在类中将T作为类型使用(如上面直接定义变量info)。

2.2 泛型接口

//泛型形参E
public interface loadResult<E>{
    //E 作为参数类型
    void returnResult(E result);
}
public interface addResult<E>{
    //E 作为类型使用
    E add();
}

泛型接口跟泛型类声明基本一样,使用的范围也是相同。

2.3 泛型方法

List不是List子类,如果想要将每一个String类型的数组元素放到一个集合中,集合的数据类型只能是String:

     String[] fruitArray = new String[]{"apple","banana","peach"};
     List<String> fruitList = new ArrayList<>();
     array2Collection(fruitArray,fruitList);
     Log.d(TAG,">>>>>> fruitList = " + fruitList);

    //代码省略...
    private void array2Collection(String[] array, List<Object> list) {
        for (String str : array) {
            list.add(str);
        }
    }

比如上面的代码,在编译环境下就会报异常。所以如果后期需要放置Integer类型,就需要再次新建Integer的集合,这样的方法功能十分有限。

为了解决这个问题,Java设计了泛型方法。泛型方法对比普通方法多了泛型形参声明,所有的泛型形参声明放在修饰符和方法返回值中间。

修饰符 <T,S> 返回值类型 方法名(形参列表){
    //方法体
}
private static <T> void add(T t) {
   Log.d(TAG, ">>>>>>" + t + "-100");
}

使用泛型方法来完成将数组数据元素放到集合中

//定义一个泛型方法,T泛型形参仅可以在该方法中使用
private <T> void array2Collection(T[] array, List<T> list) {
    for (T str : array) {
            list.add(str);
    }
}
//泛型方法使用
String[] fruitArray = new String[]{"apple", "banana", "peach"};
List<String> fruitList = new ArrayList<>();
Integer[] integers = new Integer[]{3};
Float[] floats = new Float[]{1.0f, 2.0f, 3.2f};
List<Number> numberList = new ArrayList<>();
array2Collection(integers, numberList);
Log.d(TAG, ">>>>>> Integer[] numberList = " + numberList);
array2Collection(floats, numberList);
Log.d(TAG, ">>>>>> Float[] numberList = " + numberList);
array2Collection(fruitArray, fruitList);
Log.d(TAG, ">>>>>> List<String> fruitList = " + fruitList);

//输出结果:
D: >>>>>> Integer[] numberList = [3]
D: >>>>>> Float[] numberList = [1.2.3.2]
D: >>>>>> List<String> fruitList = [apple, banana, peach]

3、泛型通配符

除了用 <T>表示泛型外,我们还经常碰到 <?>这种形式的泛型, ? 被称为通配符。

通常通配符的出现是为了指定泛型中的类型范围。通配符简分为 3 种形式:被称作无限定的通配符、被称作有上限的通配符、被称作有下限的通配符。

3.1、无限定通配符

无限定通配符经常与容器类配合使用,它其中的 ? 其实代表的是未知类型,所以涉及到 ? 时的操作,一定与具体类型无关。

public static void showShapes(List<?> shapeList) { 
}

<?>存在时,其实List 对象丧失了 add() 方法的功能,当调用时编译器不通过。

public static void showShapes(List<?> shapeList) { 
    shapeList.add(new Circle());//报错 ? cannot applied to Circle
}

<?>提供了只读的功能,也就是它删减了增加具体类型元素的能力,只保留与具体类型无关的功能。它不管装载在这个容器内的元素是什么类型,它只关心元素的数量、容器是否为空。

3.2、上界通配符

上限通配符用于在方法中放宽对变量类型的限制。代表类型 T 及 T 的子类。

上面的方法改为:

public static void showShapes(List<? extends Shape> shapeList) {
     for (Shape shape:shapeList){
         Log.i(TAG,"shape="+shape.getShape());
     }
}

showShapes(circleList);//可以运行
showShapes(triangleList);//可以运行

shapeList.add(null);//不报错
shapeList.add(new Circle());//报错 Shape cannot applied to Circle

可以对列表中的?对象进行操作了。但是,注意的是对于上界泛型列表,除空之外,我们不允许将任何对象添加到列表中。

3.3、下界通配符

上界泛型列表,无法添加对象,如果需要添加,可以用下界通配符实现。

public static void showShapes(List<? super Shape> shapeList) {
    shapeList.add(new Circle());
    shapeList.add(new Circle());
}

 最后

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

不用多说,相信大家都有一个共识:无论什么行业,最牛逼的人肯定是站在金字塔端的人。所以,想做一个牛逼的程序员,那么就要让自己站的更高,成为技术大牛并不是一朝一夕的事情,需要时间的沉淀和技术的积累。

现在竞争这么激烈,只有通过不断学习,提高自己,才能保持竞争力。

对于一些不知道学习什么,没有一个系统路线的程序员,这里给大家提供一些学习资料

需要的小伙伴,可以一键三连,点击这里获取免费领取方式

《Java核心知识点合集(283页)》

内容涵盖:Java基础、JVM、高并发、多线程、分布式、设计模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、mysql、RabbitMQ、Kafka、Linux、Netty、Tomcat、数据库、云计算等

《Java中高级核心知识点合集(524页)》

《Java高级架构知识点整理》

《Docker从入门到实践》

《spring could 学习笔记》

《JVM与性能调优知识点整理》

《MySQL性能调优与架构设计解析文档》305页

nginx入门到实战》319页

《Java并发编程》385页

《1000道 互联网Java工程师面试题 (485页)》

需要的小伙伴,可以一键三连,点击这里获取免费领取方式 

以上是关于泛型是什么?它在java中又有哪些必要性?的主要内容,如果未能解决你的问题,请参考以下文章

C#中的泛型是啥意思?

Java 泛型是什么?一文带你吃透泛型

java中泛型是啥意思,作用是啥?

[ Java面试题 ]泛型篇

01泛型是什么?——《Android打怪升级之旅》

C# 泛型是引用类型还是值类型,是根据啥判断?