2010-12-05

Java Class.cast的问题

  以前没觉得java的类型系统有什么问题.
  不过就两条机制在,box/unbox的问题而已.

  当然,接触java的时间不算久,所以没有经历过那段没有autobox的日子.

  不过,从一些例子看来,autobox也未必是好事情.
  比如这段代码

  int n = 0;
  int another = int.class.cast(n);


  从表面上看来,这应该是一段很符合表面语义的代码.
  一个类型转换,而且应该是没什么问题的.

  只是,由于cast的函数声明参数是一个Object.
  而在java里,primitive和object是两套类型系统.
  
  在java引入autobox之前,这段代码应该是会编译错误的.
  但是,有了autobox之后,没有编译错误,但是肯定会有运行错误.

  在调用的时候,java隐式地将int box成java.lang.Integer.
  
  想想,既然有autobox,那么应该也问题不大.
  最多返回Integer之后,再unbox隐式转换回int.

  但问题是,cast做了一些类型兼容的检测.
  从jvm的角度来说,int和Integer是两个不兼容的类型.
  于是,在调用cast的时候,会抛一个ClassCastException.

  也就是说,在java两套类型系统存在的前提下,
  primitive.class.cast()的行为就变得有些古怪了.
  
  一些语义上看上去无误的代码,也许运行时就必然要抛异常.
  
  当然,对于这类可以显式调用的也许可以避免.
  但程序复杂了就难免会有这样那样的trick/trap.

  比如:

  void  Type method(Class type){
       ....
      return type.class.cast(value);
  }
  
  这段代码.

  初看上去似乎还行.实现了某种程度的模板/复用.
  但是,注意那个cast.
  
  所以,很难保证在实际情况下不会出现这种box/unbox带来的cast的问题.

  于是,解决方案就是尽量避免使用cast做转型,或者时刻记住box/unbox的问题.
  
  问题是,如果不用cast转型就要自己写个检测null的东西了.
  这当然不是什么大问题.

  最好的方法估计还是统一一下类型系统吧.
  做接口的时候也许最好是使用非primitive的类型吧.
  这样或许能够避免一些问题.
  
  虽然在诸如做序列化的时候,非primitive类型会有一些额外开销.
  不过,真要做序列化的话,这些开销也是可以避免的.

  比如,最直接的一堆if,else做对应的类型映射.
  
  总而言之,java的两个类型系统是个头疼的存在.
  不仅存在陷阱,还使得在做reflect的时候经常让人崩溃.

  在这方面,后来者C#做得貌似就好得多了.

  比较,Java老了.

  语言这东西,也许不是说年代越久越成熟.

  毕竟,需要解决的总是新问题.

没有评论:

发表评论

爽文

去看了好东西. 坦白说,多少是带着点挑刺的味道去的. 毕竟打着爱情神话和女性题材的气质,多多少少是热度为先了. 看完之后倒是有些新的想法. 某种程度上来说,现在的年轻人或者说声音就像小叶. 只要说点贴心的话就能哄好. 也是那种可以不用很努力了. 留在自己的舒适区避难所小圈子抱团就...