在没有可变类型的情况下,是否存在不变类型参数的情况?

时间:2020-03-26 03:10:29

标签: java generics types immutability covariance

Java数组不是完全类型安全的,因为它们是协变的:ArrayStoreException可以出现在别名数组上。另一方面,Java集合的类型参数是不变的:例如,List<Thread>不是List<Runnable>的子类型(可能有点违反直觉)。 动机似乎与List和其他集合为可变有关,因此,为了使类型系统保持理智,其类型参数必须必须不变。

如果一种编程语言仅支持 immutable 类型,那么类型参数为协变或协变(但从不不变)的类型系统可以工作吗?换句话说,要使用Scala的方差表达方式,可以使用List[+E]Function[-T, +R]Map[+K, +V]等。 我知道有些旧语言(例如GNU Sather)似乎只支持协/反变量参数类型。

我的一般问题是:在一个完全不可变的数据类型的世界中,是否存在一种情况,特别需要一个不变参数类型(与协变或对变相反)?是否有一些不可变数据结构的示例,这些数据仅在使用不变类型参数的情况下才是正确的?

1 个答案:

答案 0 :(得分:2)

因此,每种类型的系统要么允许某些声音程序,要么禁止某些声音程序,或者两者都允许(这是Rice's theorem的结果),所以一个很好的可行假设是,是的,您提出的任何限制都是必然的。排除一些原本可以允许的声音程序。另一方面,人类是非常聪明的,因此从另一个意义上讲,答案是否定的:如果添加您所描述的狭窄,那就可以了,人们会在需要时找到解决办法。 (当然,有时他们会想出的解决方法是您不喜欢的方法,例如放弃您的语言。)

但是我认为您真正要问的是一个令人信服的案例:一个实际的示例,在此示例中,可以选择直接支持该示例还是坚持要求所有类型参数为无论是协变还是逆变,您的直觉都会告诉您放弃提案,以便您可以直接支持该示例。

由于您已经确定了类型参数不能协变的各种情况和类型参数不能协变的各种情况(例如,Function [-T,+ R]很好,但是相反完全是不合理的),一种好的方法是搜索使用相同类型参数的情况两次,一次以一种不可协变的方式,一次以一种无法协变的方式。矛盾的。一个简单的例子是UnaryOperator [T] <:Function [T,T],类似于Java的java.util.function.UnaryOperator ,其'apply'方法返回的类型与接受的类型相同。 UnaryOperator [String]不能用作UnaryOperator [Object](因为您不能将其传递给任意对象),但是UnaryOperator [Object]也不能用作UnaryOperator [String](因为即使您将其传递给String,它也可能会返回一些不同的Object。)

获得更加充实的现实示例。 。 。想象一个二叉搜索树TreeMap [K,+ V] <:Map [K,V],类似于Java的java.util.TreeMap 。大概我们想支持诸如“ firstKey”,“ floorEntry”和“ iterator”之类的方法(或至少其中一些方法),因此我们不能使K成为协变:TreeMap [Object,Foo]可以”不能用作TreeMap [String,Foo],因为当我们检索键时,键可能不是String。

由于它是二叉搜索树,因此它在内部需要一个Comparator [K],这立即使K变得协变变得棘手:如果您使用TreeMap [String,Foo]作为TreeMap [Object,Foo],那么您将隐式使用Comparator [String]作为Comparator [Object],这是行不通的。现在,由于映射表肯定只包含字符串键,因此“ get”方法可以通过在使用Comparator [String]进行调用之前预先检查键的类型来解决此问题。但是'floorEntry'和'ceilingEntry'方法仍然是一个问题:哪个条目位于无法与地图中的键进行比较的任意对象的“之前”或“之后”?

即使您已经说过地图是不可变的,您仍然可能想要某种“放置”方法,只是一种纯粹的功能,可以返回地图的修改后的副本。 (纯正的功能性红黑树支持与可变树相同的不变性和最坏情况的渐近时间复杂度,因此,除了类型系统之外,这当然是合理的事情。)但是,如果TreeMap [String,Foo]可以用作TreeMap [Object,Foo],那么其“ put”方法需要支持返回包含 non -String键的二进制搜索树-即使其Comparator [String]没有定义顺序这样的键。

(在一条评论中,您提到Scala实际上使用不变键类型定义Map [K,+ V]。我从未使用过Scala,但我敢打赌,这就是为什么。)

相关问题