Java 泛型优点之编译时类型检查

使用泛型代码要比非泛型代码更有优势,下面是 Java 官方教程对泛型其中一个优点的介绍:

“Stronger type checks at compile time.
A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.”

现在我有两点 疑问

1、 在使用泛型时能在编译时被检测出的问题,在未使用泛型时是怎样的情况?即怎样才会出现这类上文中最后一句提到的不是更容易解决的运行时错误?(以代码举例)

2、 Java 如何提供这种编译时的更强的类型检查(第一句)。

### 解决

在 Java 还未明确的实现泛型机制之前,是具有泛型能力的,只不过没有进行语法层次上的包装。比如以容器举例。

容器的中的元素基本类型都是 Object,而由于 Java 的设计理念,Java 中所有类默认都是继承于 Object 的,所以容器中的每一个元素都可 hold 任意对象的实例。

代码如下:

ArrayList list = new ArrayList();
list.add(new String("over"));
list.add(new String("loard"));
...

示意图如下:

当我们提取容器中的某一个 Object 元素时,我们只能访问到 Object 对象作用域内的实例和方法。为了访问更加具体的对象(比如上图中的 String)的方法或者实例域,我们需要告诉编译器将 Object 引用转换为 String 类型(Object 引用只能访问 String 对象的一个子集,即定义在 Object 对象中的部分。即便我们的确有一个 String 对象)。当这种转换符合继承层级时(String 是 Object 的子类),转化即可以通过编译(只是通过编译)。

String str = (String)list.get(i);

自然地,现在我们可以通过 str 访问 String 对象的方法和实例域。但是这里其实是存在潜在的问题的。Object 引用能够 hold 任意对象,那么在这个例子的容器中,意味着我们可以将其他 Object 的子类类型的对象传递给容器的元素:

list.add(new Integer(1));	//通过编译

然后当我们再次执行类型转换时,编译时没问题(因为实际是 String(Object) ),但程序将会在运行时抛出一个异常。

String str = (String)list.get(i); 	//抛出 ClassCastException 异常

尽管异常机制会提醒我们程序发生了我们未预期的情况,并将这些错误反馈给我们,然而如果问题能在编译时被解决,我们更希望在编写代码时就将错误避免掉。

当 Java 引入泛型机制后,这一目标可以被实现。Java 的泛型机制主要特点便是在原来的类型转化机制上增加类型参数和类型擦除机制。

所以当我们再次使用容器时,我们将给它传递一个类型参数:

ArrayList<String> list = new ArrayList<>();

这样当我们将不是 String 类型的对象传递给容器的元素时,编译器将会提示我们类型错误。如此一来,之前的类型转换错误就被阻挡在了编译时期。

但是,Java 为了向前兼容使用普通的类型转换的代码而采用的擦除机制并不是很强大(相比 C++)。

比如对于泛型函数来说,使用擦除机制的泛型似乎并没有带来什么改观(类型安全方面)。

类型擦除的例子:

public <T extend SomeObject> f(T t) {		//默认 T 继承于 Object
T a = t;
Sysyem.out.println(a);
}

当我们对这个方法调用后,编译器将进行对类型的擦除,经编译器处理后的代码如下:

public SomeObject f(SomeObject t) {
SomeObject a = t;
Sysyem.out.println(a);
}

由于编译器在编译时将我们传递的类型信息擦除掉(无法获得类型信息),所以一旦我们进行不合法的类型转换,编译器也不会察觉:

public <T extend Object> f(T t) {
...
String str = (String)t;
...
}
//类型擦除后
public Object f(Object t) {
...
String str = (String)t; //编译完全没问题
...
}

当我们调用该方法:

f(new Integer(1));		//在运行时将抛出一个 ClassCastException

对比 C++ 的模板,C++ 将在编译时通过传递的类型参数检测到存在非法的类型转换(C++ 元编程具有将运行时检测迁移到编译期的能力)。



所以,问题应该算是被解决了(虽然有些简陋和仓促)。

参考资料:

泛型类型擦除

https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html

Java 泛型类型安全

https://stackoverflow.com/questions/44841156/java-generics-type-safety

Java 中的类型转换

https://stackoverflow.com/questions/5289393/casting-variables-in-java

C++ 和 Java 中的泛型机制的不同

https://stackoverflow.com/questions/36347/what-are-the-differences-between-generic-types-in-c-and-java

运行时 VS 编译时

https://stackoverflow.com/questions/846103/runtime-vs-compile-time

《Java 编程思想》第四版 通过异常处理错误 (为什么编译时解决问题要比运行时解决问题要好的原因之一)

作者:Skipper

出处:http://www.cnblogs.com/backwords/p/9336714.html

本博客中未标明转载的文章归作者 Skipper 和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Java 泛型优点之编译时类型检查的更多相关文章

  1. Java泛型函数的运行时类型检查的问题

    在一个数据持久化处理中定义了数据保存和读取的 泛型函数的,但是在运行时出现类型转换错误,类型不匹配,出错的位置不是load方法,而是在调用load方法之后,得到了列表数据,对列表数据进行使用时出现的. ...

  2. 编译期类型检查 in ClojureScript

    前言  话说"动态类型一时爽,代码重构火葬场",虽然有很多不同的意见(请参考),但我们看到势头强劲的TypeScript和Flow.js,也能感知到静态类型在某程度上能帮助我们写出 ...

  3. java多态的向上转型与向下转型(与编译时类型与运行时类型有关)

    1.编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定. 当编译时类型和运行时类型不一致时,就会出现所谓的多态. 因为子类是一个特殊的父类,因此java允许把一个子类对象直接 ...

  4. Java泛型总结---基本用法,类型限定,通配符,类型擦除

    一.基本概念和用法 在Java语言处于还没有出现泛型的版本时,只能通过Object是所有类型的父类和类型强制转换两个特点的配合来实现类型泛化.例如在哈希表的存取中,JDK1.5之前使用HashMap的 ...

  5. java 泛型的内部原理:类型擦除以及类型擦除带来的问题

    一.Java泛型的实现方法:类型擦除前面已经说了,Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首要前提是理解类型擦出(ty ...

  6. Java 泛型 四 基本用法与类型擦除

    简介 Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型.泛型可以用于类.接口.方法,通过使用泛型可以使代码更简单.安全.然而 Java ...

  7. 8. 多态——编译时类型&运行时类型

    一.引用变量的两种类型 1. 编译时类型:由声明该变量时使用的类型决定 2. 运行时类型:由实际赋给该变量的对象决定 如果编译时类型和运行时类型不一致,就可能出现多态. class BaseClass ...

  8. java如何在eclipse编译时自动生成代码

    用eclipse写java代码,自动编译时,如何能够触发一个动作,这个动作是生成本项目的代码,并且编译完成后,自动生成的代码也编译好了, java编辑器中就可以做到对新生成的代码的自动提示? 不生成代 ...

  9. 一句话,讲清楚java泛型的本质(非类型擦除)

    背景 昨天,在逛论坛时遇到个这么个问题,上代码: public class GenericTest { //方法一 public static <T extends Comparable< ...

随机推荐

  1. vue2.0环境安装

    参考网站http://www.open-open.com/lib/view/open1476240930270.html (以上博客vue init webpack-simple 工程名字<工程 ...

  2. 超详细的Web前端开发规范文档

    规范目的为提高团队协作效率, 便于后台人员添加功能及前端后期优化维护, 输出高质量的文档, 特制订此文档. 本规范文档一经确认, 前端开发人员必须按本文档规范进行前台页面开发. 本文档如有不对或者不合 ...

  3. SpringBootTest单元测试实战、SpringBoot测试进阶高级篇之MockMvc讲解

    1.@SpringBootTest单元测试实战 简介:讲解SpringBoot的单元测试 1.引入相关依赖 <!--springboot程序测试依赖,如果是自动创建项目默认添加--> &l ...

  4. PP图和QQ图

     一. QQ图      分位数图示法(Quantile Quantile Plot,简称 Q-Q 图)       统计学里Q-Q图(Q代表分位数)是一个概率图,用图形的方式比较两个概率分布,把他们 ...

  5. jvm系列六、windows用jdk自带工具jps、jstack找出性能最差的代码

    一.运行程序TestGC 二.用jps找出当前应用的进程号PID 到jdk安装目录的bin目录下输入: jps -l PID为1264 三.启动Process Explorer(下载地址:https: ...

  6. WPF 未能加载文件或程序集“CefSharp.Core.dll”或它的某一个依赖项

    1.检查代码不存在问题,最后找到问题,Nut管理包没有安装CefSharp.wpf. 2.安装对应的版本即可.

  7. SQLServer语言之DDL,DML,DCL,TCL

    数据库语言分类 SQLServer   SQL主要分成四部分: (1)数据定义.(SQL DDL)用于定义SQL模式.基本表.视图和索引的创建和撤消操作. (2)数据操纵.(SQL DML)数据操纵分 ...

  8. centos 命令和

    一.远程工具 Window系统上 Linux 远程登录客户端有SecureCRT, Putty, SSH Secure Shell.TightVNC... 重点推荐一款 FinallShell,一般人 ...

  9. 提高delete效率方法

    1 open c_1;   loop     fetch c_1 bulk collect       into t2 limit 100000;     exit when c_1%notfound ...

  10. Android:Service

    Android Service: http://www.apkbus.com/android-15649-1-1.html android service 的各种用法(IPC.AIDL): http: ...