为什么在将多个类型的项放入数组时,Swift不会推断为Any类型?

7

当我使用Xcode 7.1开发Swift 2.2时,有两种情况让我感到困惑,请看下面的例子,谢谢。

首先,当我导入Foundation后,我声明了一个testArray,其中包含两个项目,一个整数类型1和一个字符串类型"hello",我的问题是为什么Swift类型推断将testArray推断为Array(NSObject)而不是Array(Any)。

import Foundation
let testArray = [1, "hello"] 
print(testArray.dynamicType) //testArray is Array<NSObject>

其次,当我删除import Foundation时,下面的代码无法编译,错误信息为“在没有更多内容的情况下表达式的类型是不明确的”,我的问题是为什么Swift在这种情况下不能将Array(Any)进行类型推断。谢谢帮助。
let testArray2 = [2, "world"]
print(testArray2) 
//can't compile, error message = "Type of expression is ambiguous without more content"
2个回答

9
/// The protocol to which all types implicitly conform.
public typealias Any = protocol<>

Any 是一个协议(protocol),所有类型都隐式地符合它 - 它本身不是具体的类型。Swift 无法推断非具体类型的数组,这就是为什么它无法推断 Any,但成功推断出 NSObject。(Int 可以转换为 NSNumberString 可以转换为 NSString - 它们都继承自 NSObject,这是一个具体类型)。

例如,考虑以下代码:

protocol Foo {}
struct Bar:Foo {}
struct Baz:Foo {}

let arr = [Bar(), Baz()] // error: Type of expression is ambiguous without more context

由于Foo是一个非具体类型,Swift无法推断出它的数组。你必须明确地告诉编译器你想要的类型:

let arr:[Foo] = [Bar(), Baz()]

对于AnyObject来说,你也会得到相同的行为(因为它是所有隐式遵循的协议,但仍然不是一个具体的类型):

class Qux {}
class Fox {}

let a = [Qux(), Fox()] // error: Type of expression is ambiguous without more context

let a1:[AnyObject] = [Qux(), Fox()] // no error

Swift无法推断非具体类型的数组,这很可能是由于语言中非具体类型的现有限制——目前大多数非平凡操作都需要具体类型。请参见这个优秀的问答示例
但说实话,您应该真正考虑的是是否需要一个Any数组。我想不出有任何实际应用程序需要一个Any数组,因为一切都隐含地符合元素,它们必须保证什么也不做(你不能在任何东西上调用特定的方法)。当然,您可以进行类型转换,但如果要获得您一开始抛弃的类型安全性,那么这样做有什么意义呢?
您应该尽可能具体指定类型。您可以为您的值构建一个包装器——这可以是一个简单的struct,用于包装一些属性,或者是一个类型擦除,以便将非具体类型包装成伪具体类型。最起码,您应该考虑创建您的数组元素符合的自己的协议。

2
最后两段提出了这里最重要的观点。老实说,NSObject(或者 AnyObject,但不能在这里使用,尽管我经常见到它被使用)并没有比 Any 更具体化多少。 - nhgrif
@originaluser2,你的解释非常有帮助,非常感谢。 - c41ux
@c41ux 很高兴能帮忙 :) - Hamish

3

因为它无法自动识别 Any 数组

如果你将其定义为以下形式,则可正常工作

let testArray2 :[Any] = [2, "world"]

Foundation库导入了NS API,自动将2转换为NSNumber,将"world"转换为NSString,并自动将其转换为NSObject数组。


2
  1. 答案:有趣的是,他问为什么,你只说因为它就是这样。
  2. 答案:如果使用 import Foundation,则 2Int"world"String。只是因为找不到本地的 Swift 类型(谁知道为什么它不能识别 [Any]),所以它会取 [NSObject]
- Binarian
2
@iGodric,嘿,你错了。1. 看看他的错误:Type of expression is ambiguous without more content,它无法自动识别,因为这就是编译器的工作方式。2. NSObject 数组必须包含 NSObjects。如果您执行 import Foundation 并执行下一个代码 print(testArray[0].dynamicType) print(testArray[1].dynamicType),输出将是 __NSCFNumber,它是 NSNumber(而不是 Int),以及 _NSContiguousString,它是 NSString 而不是 String。感谢您的“有趣”评论。 - Daniel Krom
1
  1. 是的,但是仅仅说“这就是编译器的工作原理”并不能算是解释。
  2. 你所说的是正确的,但是你在我的评论中做了一些假设,而我并没有这样说。是的,在导入Foundation并创建一个包含多种类型的数组时,2NSNumber,而"world"NSString,但这仅仅是因为推断类型是[AnyObject]。但是导入Foundation并不会自动将2转换为NSNumber,这就是我想要表达的。你的回答可能会误导人。我的第一条评论可能听起来有点严厉。
- Binarian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接