Swift函数中可选泛型参数的默认值

26

可以给可选的泛型参数设置默认值吗?

我正在尝试做这样一件事:

func addChannel<T>(name: String, data: T? = nil) -> Channel {

}

let myChannel = addChannel("myChannelName")

但是我收到了一个错误,提示:

无法推断泛型参数'T'

这是否意味着我所尝试的操作是不可能完成的?

4个回答

43

按照你现在的方式是不可能做到的。仅凭上面的代码,T 是什么类型?正如编译器所说的那样,它无法确定(我也不能确定,因为数据不在那里)。

针对具体问题的解决方法是重载而不是使用默认值:

func addChannel<T>(name: String, data: T?) -> Channel { ... }

func addChannel(name: String) -> Channel { ... }

let myChannel = addChannel("myChannelName")

但这引发了一个问题,你在这里做什么。你会认为Channel应该是Channel<T>。否则,你对data做什么呢?如果不使用Any(强烈建议避免),很难看出你的函数除了忽略data之外还能做什么。
有了Channel<T>,你可以直接使用默认值,但必须提供类型:
func addChannel<T>(name: String, data: T? = nil) -> Channel<T> { ... }
let myChannel: Channel<Int> = addChannel("myChannelName")

否则,编译器将不知道您正在创建哪种类型的通道。
(更新〜Swift 5.2)
有时,您希望T具有默认类型。您可以通过重载来实现。例如,您可能希望默认类型为Never。在这种情况下,您需要添加以下重载:
func addChannel<T>(name: String, data: T? = nil) -> Channel<T> { ... }
func addChannel(name: String) -> Channel<Never> { 
    addChannel(name: name, data: Optional<Never>.none)
}

有了这个,您可以更简单地调用:

let myChannel = addChannel(name: "myChannelName") // Channel<Never>

好的,那就说得通了 - 我只是误解了如何使用泛型。我的用例是 data 可以是任何类型的字典、整数或字符串,或者它可以为 nil。这样做的首选方式是将 data: AnyObject? = nil,然后在函数内部执行数据类型检查吗? - hamchapman
2
最好的方法不是使用“任何类型的字典、整数或字符串”。要确定它实际上是什么类型。这可能是一个“数据”类型,可以包含其中任何一种。您可以将其实现为带有关联数据的枚举。这样,您可以确保收到您期望的内容,而不仅仅是“AnyObject”。根据我的经验,当您开始在系统中获得AnyObject?时,事情很快就会变得糟糕。即使不考虑设计问题,编译器也会对此感到不满。 - Rob Napier
有关枚举类型的更多信息,请参见 https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html。在Swift中,枚举类型是一种关键类型。 - Rob Napier
如果您需要桥接到ObjC并且不能使用关联数据,那么仍然应该将其包装到一个类中,该类可以仅使用您期望的内容进行初始化。只有在您真正意味着“我不在乎;发送任何东西给我”时才使用AnyObject。 - Rob Napier
使用枚举类型会怎么样呢?我似乎无法让它正常工作。更新没关系,我想我已经解决了。 - hamchapman
或者不是这样...不确定如何使case语句工作。 - hamchapman

3

我遇到了同样的问题。虽然传统上不能使用通用默认值(完全忽略参数),但我更喜欢下面的方法而不是实现重载:

let myChannel=addChannel("myChannelName", data: Optional<Any>.none)

1

我结合现有答案的方法,既可以在没有可选参数的情况下调用函数,又可以避免重复实现该函数。

func addChannel(name: String) -> Channel {
    addChannel(name: name, data: Optional<Any>.nil)
}
func addChannel<T>(name: String, data: T? = nil) -> Channel {
    ...
}

如上所述,您应避免使用 Any 。在我的情况下,我知道 T 是一个 Encodable ,因此我使用以下代码:

struct DefaultEncodable: Encodable {}

func function1(name: String) {
    addChannel(name: name, data: Optional<DefaultEncodable>.nil)
}
func function1<T: Encodable>(name: String, data: T? = nil) {
    ...
}


1

我在定义一个有大量泛型的复杂函数时遇到了类似的问题,如果使用重载将会导致无法管理的变化。以下是代码:

struct Channel{
    //Your code here
}

class ChannelManager{
    static let typedNil : Int? = nil //<--- This is what eliminates the error "Argument for generic parameter 'T' could not be inferred"
    
    public static func addChannel<T>(name: String, data: T? = typedNil) -> Channel{
        //Your code here
        return Channel() //To demonstrate that code compiles
    }
}

通过以这种方式声明typedNil并将其用作默认参数,预期的默认值仍然是nil,但编译器将其视为nilint,因此能够推断出类型,避免了添加附加协议符合现有类型或要求重载的要求。请注意,将typedNil声明为static,否则编译将失败,并显示错误“无法使用实例成员'typedNil'作为默认参数”。

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