Java';与C#和#x27相比,s使用场地差异;s申报地点差异?

Java';与C#和#x27相比,s使用场地差异;s申报地点差异?,c#,java,generics,variance,C#,Java,Generics,Variance,我的理解是,在C#中为泛型指定差异发生在类型声明级别:当您创建泛型类型时,您为类型参数指定差异。另一方面,在Java中,在使用泛型的地方指定差异:当您创建某个泛型类型的变量时,您指定其类型参数的变化方式 每个选项的优缺点是什么?大多数人似乎更喜欢声明站点差异,因为它使库的用户更容易使用(虽然对库开发人员来说有点困难,但我认为库开发人员必须考虑差异,不管差异实际上是在哪里编写的。) 但是请记住,Java和C都不是好的语言设计的例子 虽然Java由于Java 5和类型擦除中兼容的VM改进而正确地使用

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


每个选项的优缺点是什么?

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

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

虽然Java由于Java 5和类型擦除中兼容的VM改进而正确地使用了variance并独立于JVM工作,但使用站点的差异使得使用有点麻烦,并且类型擦除的特定实现受到了当之无愧的批评

C#的声明站点差异模型减轻了库用户的负担,但在引入具体化泛型的过程中,他们基本上将差异规则构建到了VM中。 即使在今天,由于这个错误,他们也不能完全支持协变/逆变(并且,非向后兼容的具体化集合类的引入将程序员分成了两个阵营)


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

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

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

同时,Scala可以使用原语类型,而无需像在C#中那样在集合中装箱,方法是使用
@specialized
注释,该注释告诉Scala编译器生成一个或多个专用于请求的原语类型的类或方法的附加实现


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

Java风格泛型的缺点

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

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

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

Java风格泛型的优势


您可以在不同的泛型参数之间获得差异。

我只想回答声明站点和使用站点差异之间的差异,因为尽管C#和Java泛型在许多其他方面有所不同,但这些差异大多与差异正交

首先,如果我没记错的话,使用站点差异严格来说比声明站点差异更强大(尽管以简洁为代价),或者至少Java的通配符更强大(实际上比使用站点差异更强大)这种增强的功率对于使用状态构造的语言非常有用,例如C java和Java(但是Scala少得多,尤其是它的标准列表是不可变的)。请考虑<代码>列表< /> >(或<代码> IISTIS/COD>)。由于它有添加E和获取E的方法,因此它相对于E是不变的,因此不能使用声明站点方差。但是,使用站点方差时,您可以说
List
,以获取声明中
List
的协变子集,
List
以获取
List
的逆变子集ion site language库的设计者必须为每个子集创建单独的接口(或类,如果您允许类的多重继承),并让
List
扩展这些接口。如果库设计者不这样做(请注意,C#的
IEnumerable
只做了
IList
协变部分的一小部分),那么你就走运了,你不得不求助于你在一种语言中所遇到的同样的麻烦,而没有任何变化

这就是使用站点继承比声明站点继承的优势。声明站点继承比使用站点继承的优势对于用户来说基本上是简洁的(前提是设计者将每个类/接口划分为协变和逆变部分)。对于像
IEnumerable
Iterator
这样的东西,最好不要每次使用接口时都指定协方差。Java通过使用冗长的语法(除了双变量,Java的解决方案基本上是理想的)使这一点特别烦人

当然,这两种语言特性可以共存