代码之家  ›  专栏  ›  技术社区  ›  munificent

Java的使用站点差异与C的声明站点差异相比如何?

  •  34
  • munificent  · 技术社区  · 14 年前

    我的理解是,在C中为泛型指定方差是在类型声明级别发生的:在创建泛型类型时,为类型参数指定方差。另一方面,在Java中,在使用泛型的地方指定variance:当创建某个泛型类型的变量时,指定其类型参数的变化方式。

    每种选择的利弊是什么?

    4 回复  |  直到 14 年前
        1
  •  38
  •   Ven    9 年前

    我只想回答声明site和use site variance之间的区别,因为虽然C和Java泛型在许多其他方面有所不同,但这些区别大多与variance正交。

    首先,如果我记得正确的话,使用site variance比声明site variance更强大(尽管代价是简洁),或者至少Java的通配符是(实际上比使用site variance更强大)。这种增强的功能对于大量使用有状态结构的语言(如C#和Java)尤其有用(但Scala的作用要小得多,特别是因为它的标准列表是不可变的)。考虑 List<E> (或 IList<E> ). 由于它有求E和求E的方法,所以它对E是不变的,因此不能使用声明点方差。但是,使用网站差异,你可以说 List<+Number> 得到的协变子集 List List<-Number> 得到 列表 . 在声明站点语言中,库的设计器必须为每个子集创建单独的接口(如果允许类的多重继承,则为类),并且 列表 扩展这些接口。如果库设计器不这样做(请注意C IEnumerable 只有协变部分的一小部分 IList ),那么你就不走运了,你必须求助于同样的麻烦,你必须在没有任何变化的语言。

    这就是使用站点继承比声明站点继承的优势。声明站点继承比使用站点继承的优势基本上是为用户简化的(前提是设计人员经过努力将每个类/接口分离为其协变和逆变部分)。比如说 不可数 Iterator ,不必每次使用接口时都指定协方差,这很好。Java通过使用冗长的语法(除了Java的解决方案基本上是理想的双变量)使这一点特别恼人。

    当然,这两种语言特征可以共存。对于自然协变或反变的类型参数(例如 不可数 / 迭代器 ),在声明中声明。对于自然不变的类型参数(例如 (I)List ),声明每次使用时所需的差异类型。不要为带有声明site variance的参数指定use site variance,因为这只会让事情变得混乱。

    还有一些更详细的问题我还没有涉及(例如通配符实际上比使用站点差异更强大),但我希望这能回答你的问题,你的内容。我承认我倾向于使用站点差异,但我试图描述在我与程序员和语言研究人员的讨论中出现的这两种方法的主要优点。

        2
  •  16
  •   soc Bhuwan Tripathi    14 年前

    大多数人似乎更喜欢声明站点差异,因为它使 用户 对于库(虽然对库开发人员来说有点困难,但我认为库开发人员必须考虑差异,而不管差异实际写在哪里)

    但是请记住,Java和C都不是好的语言设计的例子。

    爪哇 由于Java5和类型擦除中兼容的虚拟机改进,使得方差正确并独立于JVM工作,使用站点方差使使用变得有点麻烦,类型擦除的特殊实现受到了应有的批评。

    C类# 声明站点差异的模型减轻了库用户的负担,但是在他们引入具体化泛型的过程中,他们基本上在虚拟机中构建了差异规则。 即使在今天,他们也不能完全支持co-/contransage,因为这个错误(而不向后兼容的具体化集合类的引入将程序员分成了两个阵营)。

    这对以CLR为目标的所有语言都构成了一个困难的限制,这也是为什么替代编程语言在JVM上更加活跃的原因之一,尽管看起来CLR具有“更好的特性”。

    让我们看看 斯卡拉 :Scala是一个在JVM上运行的完全面向对象、功能性的混合体。 它们使用类似Java的类型擦除,但是泛型和(声明站点)差异的实现都比Java(或C#)更容易理解,更直接和强大,因为VM没有对差异的工作方式强加规则。Scala编译器检查方差符号,并可以拒绝不健全的源代码 编译时 而不是在运行时抛出异常,而生成的.class文件可以从Java无缝地使用。

    声明站点方差的一个缺点是,在某些情况下,它似乎使类型推断变得更困难。

    同时,Scala可以使用原始类型,而不必使用C语言中的集合来装箱它们。 @specialized 告诉Scala编译器生成一个或多个特定于所请求原语类型的类或方法的附加实现的注释。

    Scala还可以通过使用清单“几乎”具体化泛型,清单允许它们在运行时检索泛型类型,就像在C#中那样。

        3
  •  1
  •   CodesInChaos    14 年前

    Java风格泛型的缺点

    一个结果是java版本只处理引用类型(或装箱值类型),而不处理值类型。IMO这是最大的缺点,因为它在许多场景中阻止了高性能泛型,并且需要手动编写特殊类型。

    它不能保证像“这个列表只包含x类型的对象”这样的不变量,并且需要在每个getter上进行运行时检查。泛型类型确实存在。

    使用反射时,不能询问泛型对象的实例它具有哪些泛型参数。

    Java风格泛型的优势

    您可以得到不同泛型参数之间的差异/可以转换。

        4
  •  0
  •   user2684301    11 年前

    Java:从Java 5开始使用site variance泛型。从1.0开始使用不同语法的已断开的协变数组。没有泛型的运行时类型信息。

    C#:从C#2.0开始使用站点差异泛型。在C#4.0中添加了申报地点差异。从1.0开始使用不同语法的已断开的协变数组(与Java问题相同)。”“具体化”泛型意味着类型信息不会在编译时丢失。

    Scala:这两种语言都使用自早期版本(至少自2008年以来)以来的站点/声明站点差异。数组不是一个单独的语言特性,因此使用相同的泛型语法和类型差异规则。某些集合是在VM级别使用JVM数组实现的,因此与Java代码相比,您可以获得同等或更好的运行时性能。

    要详细说明C#/Java数组类型安全问题:可以将Dog[]强制转换为Pet[],然后添加C a t并触发编译时未捕获的运行时错误。Scala正确地实现了这一点。