`
javatar
  • 浏览: 1685733 次
  • 性别: Icon_minigender_1
  • 来自: 杭州699号
社区版块
存档分类
最新评论

关于Java泛型违反Liskov原则

阅读更多
Java5 增加的泛型语法,使类型模板的应用得到了提升,但它的运行期擦拭的做法(为向前兼容),令人诟病,
使得一个Map集合,通过反射拿到的集合元素的泛型类型,不是实际使用类型,而是K和V(字节码编译期保留)。
另一个有争议的地方是:
泛型违反了里氏代换原则(Liskov's Substitution Principle),即:子类应该在任何地方都能替换父类。
假设一个函数:
void xxx(List<Object> list);

调用:
List<String> list = ...
xxx(list); // 编译出错

当然,你可以使用下面的变通方式就不会出错:
void xxx(List<? extends Object> list);


主要的问题在于:List<String> 是不是 List<Object> 的子类?
我觉得应该是,至少“人”认为是,面象对象的主要目的是什么?就是让人理解程序,而不是机器。
而官方给出的答案却是:List<String> 不是 List<Object> 的子类
Java泛型规格说明书 写道

让我们测试一下我们对泛型的理解。下面的代码片断合法么?

List<String> ls = new ArrayList<String>(); //1

List<Object> lo = ls; //2

第1行当然合法,但是这个问题的狡猾之处在于第2行。

这产生一个问题:

一个String的List是一个Object的List么?大多数人的直觉是回答:“当然!”。

好,在看下面的几行:

lo.add(new Object()); // 3

String s = ls.get(0); // 4: 试图把Object赋值给String

这里,我们使用lo指向ls。我们通过lo来访问ls,一个String的list。我们可以插入任意对象进去。结果是ls中保存的不再是String。当我们试图从中取出元素的时候,会得到意外的结果。

java编译器当然会阻止这种情况的发生。第2行会导致一个编译错误。

总之,如果Foo是Bar的一个子类型(子类或者子接口),而G是某种泛型声明,那么G<Foo>是G<Bar>的子类型并不成立!!

这可能是你学习泛型中最难理解的部分,因为它和你的直觉相反。

这种直觉的问题在于它假定这个集合不改变。我们的直觉认为这些东西都不可改变。

举例来说,如果一个交通部(DMV)提供一个驾驶员里表给人口普查局,这似乎很合理。我们想,一个List<Driver>是一个List<Person>,假定Driver是Person的子类型。实际上,我们传递的是一个驾驶员注册的拷贝。然而,人口普查局可能往驾驶员list中加入其他人,这破坏了交通部的记录。

为了处理这种情况,考虑一些更灵活的泛型类型很有用。到现在为止我们看到的规则限制比较大。


上面所描述的语义限制根本没有意义,如果想限制用户在List<String>中加入Object, 因为泛型的运行期擦拭,等于白做。
因为想做这个语义上的限制,而牺牲直观的理解非常不值,加一个"?"问号作为替代方案,只会使泛型更复杂,使用也不方便。
用户必须在"?"问号与"Object"间绕来绕去,烦也不烦,或许可以问:"?"问号等于"Object"吗?呵呵,maybe.
2
1
分享到:
评论
3 楼 mercyblitz 2010-03-15  
其实我觉得擦写是没有意义,导致了运行时参数类型无法获取。

2 楼 javatar 2008-11-11  
或许文章的标题有些问题,Liskov原则是继承体系的基本,Sun不会去违反它的,只是Sun将List<String>解释成不是List<Object>的子类,而是List<?>的子类,有些让人费解,无意义的增加了泛型的复杂性。
1 楼 非常菜 2008-11-10  
找到一个定义 Liskov Substitution Principle LSP:
一个软件实体如果使用的是一个基类的话那么一定适用于其子类,而且它察觉不出基类对象和子类对象的区别。也就是说,在软件里面,把基类都替换成它的子类,程序的行为没有变化。
出处:http://aladdin.iteye.com/blog/40810
说的是,对于基类的使用可以用子类来代替,程序行为未改变。

就拿Object和String的例子,我觉得可以这么套用定义:
所有可以使用Object的地方,可以用String代替。
但,所有可以用List<Object>的地方,能否用List<String>代替?泛型的回答是:否。

楼主的问题在于,泛型违反LSP的做法是否值得?

从我个人的角度考虑,有了泛型之后,对于型别检查变得更方便了,虽然违反LSP,但是对于开发的效率和减少开发中关于型别不一致导致的低级错误的减少是大有裨益的。另外,在泛型中引入继承机制,也是为了增加java的灵活性。
我想Sun的工程师们,也是经过较长时间考虑才做出这样设计的吧。

相关推荐

    Java泛型编程指南.pdf

    Java泛型编程指南.pdf 此文章译自SUN的泛型编程指南

    Java泛型和集合

    Java Generics and Collections 英文版,详细描述java 泛型技术

    JAVA泛型加减乘除

    这是一个使用JAVA实现的泛型编程,分为两部分,第一部分创建泛型类,并实例化泛型对象,得出相加结果。 第二部分用户自行输入0--4,选择要进行的加减乘除运算或退出,再输入要进行运算的两个数,并返回运算结果及...

    java 泛型接口示例

    java 泛型接口示例 java 泛型接口示例 java 泛型接口示例

    java 泛型类的类型识别示例

    java 泛型类的类型识别示例 java 泛型类的类型识别示例 java 泛型类的类型识别示例

    Java泛型三篇文章,让你彻底理解泛型(super ,extend等区别)

    Java 泛型详解 Java 泛型是 Java SE 5.0 中引入的一项特征,它允许程序员在编译时检查类型安全,从而减少了 runtime 错误的可能性。泛型的主要优点是可以Reusable Code,让程序员编写更加灵活和可维护的代码。 ...

    java 泛型方法使用示例

    java 泛型方法使用示例 java 泛型方法使用示例 java 泛型方法使用示例

    1.java泛型定义.zip

    1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1....

    java泛型技术之发展

    java泛型技术之发展,学习JAVA 泛型的不错东东

    关于java泛型的讲解

    java 泛型 java 泛型 java 泛型 java 泛型

    4.java泛型的限制.zip

    4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip...

    Java泛型的用法及T.class的获取过程解析

    主要介绍了Java泛型的用法及T.class的获取过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    java泛型总结

    深入理解java泛型,包括类名泛型的定义,方法泛型定义,泛型的返回

    关于C#、java泛型的看法

    谈谈关于Microsoft Visual Studio 2008中C#和java泛型的区别

    java泛型学习ppt

    java,学习java泛型,java培训之泛型.pptxjava培训之泛型.pptxjava培训之泛型.pptxjava培训之泛型.pptx

    很好的Java泛型的总结

    很好的Java泛型的总结,看完之后你一定会知道java泛型的底层机制,你一定会学会Java泛型!

    关于java基础的泛型的练习

    关于java基础的泛型的练习

    Java泛型使用详细分析.pdf

    Java 泛型使用详细分析 Java 泛型是 Java 语言中的一种类型系统特性,允许开发者在编译期检查类型安全,以避免在运行时出现类型相关的错误。在本文中,我们将详细介绍 Java 泛型的使用方法和实现原理。 一、泛型的...

    java泛型详解.pdf

    java泛型详解.pdf

Global site tag (gtag.js) - Google Analytics