Swift:方法重载只在返回类型上有所不同

时间:2015-07-29 22:46:29

标签: ios swift swift2

我一直在看Swift类,其中定义了两种方法,它们的返回类型不同。我不习惯使用允许这种语言的语言(Java,C#等),所以我去寻找描述它如何在Swift中工作的文档。我在任何地方都找不到任何东西。我本来期望在Swift书中有关于它的整个部分。这记录在哪里?

这是我正在谈论的一个例子(我正在使用Swift 2,FWIW):

class MyClass {
    subscript(key: Int) -> Int {
        return 1
    }

    subscript(key: Int) -> String {
        return "hi"
    }

    func getSomething() -> Int {
        return 2
    }

    func getSomething() -> String {
        return "hey"
    }
}

测试:

    let obj = MyClass()    

    //let x = obj[99]
    // Doesn't compile: "Multiple candidates fail to match based on result type"

    let result1: String = obj[123]
    print("result1 \(result1)")  // prints "result1 hi"

    let result2: Int = obj[123]
    print("result2 \(result2)") // prints "result2 1"

    //let x = obj.getSomething()
    // Doesn't compile: "Ambiguous use of 'getSomething'"

    let result3: String = obj.getSomething()
    print("result3 \(result3)")  // prints "result3 hey"

    let result4: Int = obj.getSomething()
    print("result4 \(result4)") // prints "result4 2"

3 个答案:

答案 0 :(得分:8)

  

这在哪里记录?

至于subscript

Language Reference / Declarations / Subscript Declaration

  

只要参数或返回类型与您正在重载的参数不同,就可以在声明它的类型中重载下标声明。

Language Guide / Subscripts / Subscript Options

  

类或结构可以根据需要提供任意数量的下标实现,并且将根据类型的值或下标括号中包含的值推断要使用的相应下标。在使用下标的时候。

我找不到任何关于重载方法或函数的官方文档。但是在Swift博客中:

Redefining Everything with the Swift REPL / Redefinition or Overload?

  

请记住,即使两个签名的返回类型不同,Swift也允许函数重载。

答案 1 :(得分:6)

函数的类型由其参数的类型及其返回值的类型决定,编译器可以根据其类型消除类似命名的函数的歧义 - 从您的示例:

subscript(key: Int) -> Int {
    return 1
}

...的类型为(Int) -> Int

subscript(key: Int) -> String {
    return "hi"
}

...的类型为(Int) -> String

- 所以虽然它们的名称相似,但是编译器可以通过如何分配返回值(或者因为它是subscript来推断调用哪一个,通过分配给该下标的值)

继续:

func getSomething() -> Int {
    return 2
}

...的类型为() -> Int

func getSomething() -> String {
    return "hey"
}

...的类型为() -> String

注意:如果您没有为编译器提供足够的信息来推断您正在调用哪个函数,那么您可能遇到麻烦的地方就是:如果你只是在没有对其返回值做任何事情的情况下调用getSomething(),就会抱怨ambiguous use of 'getSomething'

编辑 - 啊,我现在在你的示例代码中看到你确实提供了一个例子,这是一个例子:)通过将返回值赋给你没有指定的常量类型(let x = getSomething())没有足够的信息让编译器整理出你正在调用的函数

编辑 编辑 - 请注意,在我开始说' 时,编译器可以按类型消除类似命名函数的歧义&# 39;,函数名称由下式确定:(1)函数的标识符,以及(2)函数的外部参数名称的标识符 - 例如,虽然以下两个函数都具有相同的类型和函数标识符,但它们是不同的函数,并且具有不同的函数名称,因为它们的外部参数名称使用的标识符不同:

func getSomething(thing: String, howMany: Int) -> String

...的类型为(String, Int) -> String,名为getSomething(_:howMany:)

func getSomething(thing: String, howManyTimes: Int) -> String

...的类型为(String, Int) -> String,名为getSomething(_:howManyTimes:)

答案 2 :(得分:0)

这是Swift的一个相当酷的方面。我目前在泛型类中使用它有多个下标。这是我创建的一个游乐场:

import Foundation

/*
 Return Type Differentiation

 This playground illustrates a rather useful capability of Swift: The ability to differentiate methods by return type; not just argument list.

 In this example, we will set up multiple subscript() methods for an aggregator/façade class that will access the contained instances in
 various ways, depending on the return type requested.
 */

// This class should win the igNoble prize for poitry.
struct A {
    let poem: [String] = ["I'm a little teapot",
                          "bloody and cut.",
                          "This is my handle.",
                          "This is my "]

    let multiplier: UInt32 = arc4random_uniform(100)    // Just a random integer from 0 to 100.
}

// This class has a few different data types that are set at instantiation time, and one static instance of A
class B {
    let stringProperty: String
    let intProperty: Int = Int(arc4random_uniform(10))
    let objectProperty: A = A()

    init(_ string: String) {
        self.stringProperty = string
    }

    // This will be used to demonstrate that we don't need to explicitly cast, if we only have one subscript method.
    subscript(_ ignoredIndex: Int) -> A {
        return self.objectProperty
    }
}

// This class acts as a façade class. It provides an interface to its contained classes as if they were direct subscripts.
class C : Sequence {
    let aArray: [B]

    init() {
        self.aArray = [B("hut"),B("butt")]
    }

    // You can have multiple subscript() methods, differentiated by return type.
    subscript(_ index: Int) -> B {
        return self.aArray[index]
    }

    subscript(_ index: Int) -> String {
        return self.aArray[index].stringProperty
    }

    subscript(_ index: Int) -> UInt32 {
        return (self[index] as A).multiplier
    }

    subscript(_ index: Int) -> Int {
        return self.aArray[index].intProperty
    }

    subscript(_ index: Int) -> A {
        return self.aArray[index].objectProperty
    }

    // These are not simple data return subscripts. In fact, there are no Float properties, so that one is made from whole cloth.
    subscript(_ index: Int) -> Float {
        return Float(self.aArray[index].intProperty) * Float((self[index] as A).multiplier)
    }

    subscript(_ index: Int) -> [String] {
        var ret: [String] = []

        let aInstance: B = self.aArray[index]

        ret = aInstance[0].poem // No need for explicit casting if we only have one subscript.

        ret[3] += self[index] + "." // This is allowed, as we know we're a String.

        return ret
    }

    // You can only have one makeIterator() method.
    func makeIterator() -> AnyIterator<[String]> {
        var nextIndex = 0

        // Return a "bottom-up" iterator for the list.
        return AnyIterator() {
            if nextIndex == self.aArray.count {
                return nil
            }
            let ret: [String]! = self.aArray[nextIndex - 1].objectProperty.poem
            nextIndex += 1
            return ret
        }
    }

    // You can have multiple methods with the same input signature, differentiated only by their output signature.
    func returnIndexedElement(_ atIndex: Int) -> Int {
        return self[atIndex]    // Note no explicit casting is necessary, here.
    }

    func returnIndexedElement(_ atIndex: Int) -> UInt32 {
        return self[atIndex]
    }

    func returnIndexedElement(_ atIndex: Int) -> A {
        return self[atIndex]
    }

    func returnIndexedElement(_ atIndex: Int) -> B {
        return self[atIndex]
    }

    func returnIndexedElement(_ atIndex: Int) -> Float {
        return self[atIndex]
    }

    func returnIndexedElement(_ atIndex: Int) -> String {
        return self[atIndex]
    }

    func returnIndexedElement(_ atIndex: Int) -> [String] {
        return self[atIndex]
    }
}

let mainObject = C()

// First, let's test the subscripts.
// We have 3 elements, so
let aObject1: A = mainObject[0]
let aObject2: B = mainObject[0]
let aString: String = mainObject[0]
let aPoem: [String] = mainObject[0]
let aInt: Int = mainObject[0]
let aUInt32 = mainObject[0] as UInt32
let aFloat = mainObject[0] as Float

// This will not work. You need to specify the type explicitly when using multiple subscripts, differentiated only by return type.
// let failObject = mainObject[0]

// However, this will work, because the class has only one subscript method defined.
let aObject2_Subscript = aObject2[0]
let aObject2_Poem = aObject2_Subscript.poem

// Next, test the accessor methods.
let bObject1: A = mainObject.returnIndexedElement(1)
let bObject2: B = mainObject.returnIndexedElement(1)
let bString: String = mainObject.returnIndexedElement(1)
let bPoem: [String] = mainObject.returnIndexedElement(1)
let bInt: Int = mainObject.returnIndexedElement(1)
let bUInt32 = mainObject.returnIndexedElement(1) as UInt32
let bFloat = mainObject.returnIndexedElement(1) as Float

// This will not work. You need to specify the type explicitly when using multiple methods, differentiated only by return type.
// let failObject = mainObject.returnIndexedElement(1)