Java 类型推导的不确定性

35 篇文章 0 订阅
19 篇文章 0 订阅

本文是读完oopsla16的一些心得

Figure 1

上面这个测试例运行的结果是,main函数中抛出一个 ClassCastException ,异常信息为 “java.lang.Integer cannot be cast to java.lang.String” 。原因简单说就是因为JVM运行时检查出了类型问题,发现代码企图将一个Integer类型的对象转化为String类型,这种转换是不允许的。

但是,这种类型问题本应该是在编译期检查出来的。这里是什么导致编译器检查不出这个类型错误?

我们模拟一下Java类型推导的过程。类型推导总是先假设程序类型是对的,然后在这个前提下得出一些条件,最后验证这些条件是否成立。用类型Z代替 Constrain<U, ? super T> 中的  ,可以得到Figure 2中的信息。

Figure 2

a)和b)是显而易见的。c)是因为upcast函数的第一个参数的类型是Constrain<A, B>, 而这里传入了一个合法的参数,参数类型为Constrain<U, ? super T>。由于将U作为Bind的类型构造符,所以得到Z==B。upcast第二个参数的类型为T,而声明的类型为B,因此得到T<:B,因为B<:A,这里A已经因为Bind<U>而变为U,因此B<:U。

对于c), d), e)3个条件,Figure-1的例子中c)是满足的,因为upcast接受了一个合法的Constrain<U, Z>类型的参数。由于c)条件满足,所以d)和e)也满足了。

但是, Figure-1的正确仅仅是一个巧合。原因是 Java类型推导验证条件的顺序是不确定的 。如果顺序的验证条件c), d), e),那么条件满足。如果先验证d),e),再验证c),那么T<:U这个条件就不能满足了。所以不同编译器在编译Figure-1时结果是不同的。

不过我个人认为,造成这种类型推导结果不一致的原因并不能完全归结于条件的验证顺序。如果Java像Ocaml一样严格的区分Some和None,那么对于Figure-1的例子,我们就不可能向upcast函数传一个合法的Constrain<U, ? super T>类型的对象。

oopsla16中还给出了一些其他例子,用于说明Java类型推导的不一致性。

Ocaml 实现

type ('a, 'b) eq = Refl : ('a, 'a) eq



let cast (type a) (type b) (eq: (a, b) eq) (x : a) : b =
  match eq with
  | Refl -> x

let lies : (int, string)eq = Obj.magic 0;;

cast lies 42

上面的Ocaml也实现了一个与Figure-1一样的问题的例子。执行后内存错直接导致ocaml解释器崩溃。不过与Java不同的是,构造 (int ,string)eq 类型需要使用 Obj.magic 作为hack手段,而Java直接使用 null 就可以。

结论

  1. Java的类型推导是难以捉摸的,因此使用时应该尽可能的给出类型参数信息。
  2. 强类型语言的确更安全一些。

     java高级进阶群:https://jq.qq.com/?_wv=1027&k=5TjDUxB

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值