【问题标题】:Swift: method overloads that only differ in return typeSwift:仅返回类型不同的方法重载
【发布时间】:2015-07-29 22:46:29
【问题描述】:

我经常看到 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"

【问题讨论】:

  • 我认为你的演示很好地解释了事情。如果编译器可以确定使用哪个函数,则允许。这不仅包括函数的名称,还包括参数的名称和类型以及返回类型。具有不同签名的函数算作不同的函数。

标签: ios swift swift2


【解决方案1】:

这是在哪里记录的?

至于subscript

Language Reference / Declarations / Subscript Declaration

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

Language Guide / Subscripts / Subscript Options

一个类或结构可以根据需要提供尽可能多的下标实现,并且将根据 值的类型或包含在下标大括号中的值来推断要使用的适当下标在使用下标的地方。

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

Redefining Everything with the Swift REPL / Redefinition or Overload?

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

【讨论】:

    【解决方案2】:

    函数的类型由其参数的类型和返回值的类型决定,编译器可以通过它们的类型来消除类似命名的函数的歧义 - 从您的示例中:

    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()) 没有足够的信息让编译器找出你正在调用的函数

    EDIT EDIT - 请注意,我首先说“编译器可以通过类型来区分类似命名的函数”,函数名称是由以下因素决定:(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:)

    【讨论】:

      【解决方案3】:

      这是 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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多