栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA-泛型篇

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA-泛型篇

最近又开始学习了,仔细看来,发现这篇在现在看来毫无营养的文章居然发布快4年了。个人工作也快3年了,感觉自己越来越像个CRUD程序猿,是时候回来翻翻了。

一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用多种类型的代码,这种刻板的限制对代码的束缚就会很大。 《JAVA编程思想》

泛型最主要的作用:告诉你自己、你的同事以及IDE,别在我需要一个Integer的时候给我一个String,谢谢! 沃知基硕德。

为什么要有泛型

泛型在JAVA语言里面出现的时间不算早,1.5之后才出现,而且JAVA的泛型一直有人说其是伪泛型,那么为什么JAVA的语言开发者要引入泛型呢,而且不想其他语言一样支持真实的泛型,而是选择使用擦除呢?

为什么要引入泛型

想知道这个问题的答案就需要知道如果没有引入泛型会有什么问题。
答案是不会有任何问题。
你的程序加不加泛型丝毫不影响其能不能在JVM里面跑,也不影响其跑的快不快,因为泛型是到不了Class的,其在编译时就会被擦除,替换了。这就是为什么很多人说JAVA里面的泛型是伪泛型。

// 对于jvm来说,下面两行代码毫无区别,他们都等于第三行代码
List allStrList = new ArrayList<>();
List allStrList = new ArrayList();

List allStrList = new ArrayList<>();
 

那为什么JAVA语言开发者还要引入泛型,并且还要弄个伪泛型呢?他们都是大神级别的人物,精通C++,JAVA,C#等语言,对于他们来说实现真正的泛型并非难事。
对于第一个问题,其实上面引述自《JAVA编程思想》的话就已经说明白了,就是为了代码的复用,C++里面的泛型甚至直接叫“模板”。此外其实还有个作用,个人甚至觉得这个作用更大,就是用来提高阅读性和帮助IDE进行类型检查,避免潜在的错误。事实上,当你按上面的第二行代码写上去,编译器是会给Raw Types警告的,需要使用@SuppressWarnings("rawtypes")进行压制。

// 如果不使用此注解进行压制,IDE将给出raw types警告
@SuppressWarnings("rawtypes")	
List allStrList = new ArrayList();

但对于第二个问题,事实上对于JVM从技术上来说,实现起来并不难,但这群大佬还是选择了放弃真泛型。其原因大致有两点:

  1. 兼容性考虑:当时是1.5,如果使用了真泛型,就会出现Python2 和 Python3的问题,无法相互兼容。但当时的JAVA已经运行在了无数设备上,还有无数的第三方库,不兼容的后果将会是灾难性的。我们可以从前不久的log4j的事情以及ie的事情来设想下此情景。
  2. 效率考虑:JAVA在很长的一段时间里,其运行效率都是捉襟见肘的,特别是在电子产业早期,相信不少同学应该都在手机上玩过JAVA游戏,以诺基亚为例,当时的内存是几百M。而如果使用真泛型,JVM就需要针对运行的程序进行记录,保存其运行类型。这一点可以从C#的身上看到影子,由于 .NET 泛型的类型参数之实际类型在运行时均不会被消除,运行速度会因为类型转换的次数增加而减慢。
怎么用泛型

泛型其实用法很简单,就下面几项:

  1. 泛型类:可以约束其非静态属性,非静态方法。比如下面代码里面的T,R。
  2. 泛型方法:
    • 静态方法:静态方法其泛型是不受类泛型约束的,比如下面这个例子里面的T,其和类上面的T是完全无关的,同样的还有静态属性是不合法的,同学可以想想是为什么?
    • 普通方法:其可以自定义泛型,也可以使用类上面的泛型
  3. 泛型接口:这个其实和泛型类大差不差,唯一的点在于其工厂模式的设计。但这属于设计模式的应用,不属于语法层面,
  4. 上边界:写法和类的继承一样也是使用extends关键字,以此约束泛型必须是某个类的之类
  5. 下边界:关键词为super,而且只能和下面的通配符?配合使用,用于约束泛型的下边界
  6. 通配符:关键词?,这是一个非常特殊的泛型类型,可以代表任何类型,你甚至可以List这种骚操作,而这某种程度来说也是唯一应该使用的用法。
public abstract class Generic {

    T[] genericTs;

    T t;

    List list;

    {
        System.out.println(genericTs.length);
        System.out.println(t);
        System.out.println(list);
    }
    
    public abstract  T[] genericArrays(
            T t, R r,
            List tList,
            Class numberClazz, Class superIntegerClazz,
            List list
    );

	// 注意此处的R继承自String,和类上面的继承自Number的R没有半毛钱关系
    public static  R staticFun(
            R r,
            List listExtends,
            List listSuper
    ) {
        return r;
    }

}

// 泛型接口的工厂模式
interface GenericInterface{
    T getInstance();
}
泛型使用的注意点
  1. 泛型是会被擦除的,其擦除后的实际对象会依据泛型的上下界而定,有上界的擦除为上界,有下界的擦除为下界,通配或者无界的擦除为Object。
  2. 普通的泛型变量只能有上界,不能有下界。
  3. 通配符可以有上界,也能有下界,但不能同时有上下界。
  4. 泛型最大的作用是告诉IDE里面的类型是什么,在编译阶段帮忙审查代码是否存在错误,尽量避免运行时发生类型错误
泛型源码和顶层设计

下图便是泛型的最顶层设计。

  • 其中的Type是1.5版本新设计的,是所有JAVA类型的祖先类,类似于Object在实例里面的地位,
  • 对于所有的JAVA文件,无论是系统自带的jar包,或者是个人开发者写的类,接口,注解等等都可以编译为Class文件,所以对于这些一概类型定义为Class
  • ?是泛型通配符(WildcardType),当进行编译的时候,其是最特殊的一个,需要考虑其上下界
  • 泛型类型(TypeVariable)就是我们常写的T, E, K, V, R这些。
  • GenericArrayType这个根据字面意思就能知道是啥,泛型数组,比如T[]这样的。
  • 被泛型化类型(ParameterizedType),这个是指List之类,本来是普通类型,但添加了泛型类型的普通类型。

例子:

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号