`
javatar
  • 浏览: 1701230 次
  • 性别: 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泛型加减乘除

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

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

    Java泛型的用法及T.class的获取过程解析 Java泛型是Java编程语言中的一种重要特性,它允许开发者在编写代码时指定类型参数,从而提高代码的灵活性和可读性。本文将详细介绍Java泛型的用法 及T.class的获取过程解析...

    很好的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编程语言中的一个关键特性,它在2004年随着Java SE 5.0的发布而引入,极大地增强了代码的类型安全性和重用性。本篇文章将深入探讨Java泛型的发展历程、核心概念以及其在实际开发中的应用。 1. **发展...

    java 泛型方法使用示例

    Java 泛型是Java SE 5.0引入的一...以上就是关于Java泛型方法的基本介绍和使用示例,希望对你有所帮助。如果你想要进一步了解或实践,可以参考提供的`GenericMethod.java`源代码文件,或者查阅`Java.jpg`中的相关图片。

    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基础的泛型的练习

    Java泛型是Java SE 5.0引入的一个重要特性,它极大地增强了代码的类型安全...在进行"关于Java基础的泛型的练习"时,可以尝试编写不同的泛型类、泛型方法,体验泛型带来的便利,并理解其背后的类型系统和类型擦除机制。

    java 泛型接口示例

    Java 泛型是Java SE 5.0引入的一项重要特性,极大地增强了代码的类型安全性和重用性。泛型接口是泛型在接口中的应用,它允许我们在接口中定义带有类型参数的方法,使得实现该接口的类可以使用不同的数据类型。下面...

    java 泛型类的类型识别示例

    综上所述,虽然Java泛型在编译后会进行类型擦除,但通过上述技巧,我们仍然能够在运行时获得关于泛型类实例化类型的一些信息。在实际开发中,这些方法可以帮助我们编写更加灵活和安全的代码。在示例文件`GenericRTTI...

    关于C#、java泛型的看法

    在编程世界中,C#和Java都是广泛应用的高级编程语言,它们都支持泛型这一强大的特性,以提高代码的类型安全性和重用性。本文将深入探讨C#和Java在泛型实现上的异同,帮助开发者更好地理解和利用这两种语言的泛型功能...

    java泛型的内部原理及更深应用

    因此,泛型在运行时并不存在,所有关于泛型的操作都在编译期间完成。 2. **边界通配符**:在处理泛型时,我们经常遇到边界通配符,如`&lt;? extends T&gt;`和`&lt;? super T&gt;`。前者表示类型参数可以是T或T的任何子类型,后...

    java泛型学习ppt

    "Java 泛型学习" Java 泛型是 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的...

Global site tag (gtag.js) - Google Analytics