Свифт: перегрузки метода, которые отличаются только типом возвращаемого

Я продолжаю видеть классы Swift, где определены два метода, которые отличаются только типом возврата. Я не привык работать на языках, где это разрешено (Java, C# и т. д.), Поэтому я пошел искать документацию, которая описывает, как это работает в Swift. Я нигде ничего не нашел. Я ожидал, что в книге Swift будет целый раздел об этом. Где это задокументировано?

вот пример того, о чем я говорю (я использую Swift 2, Чистки рядов):

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

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

    func getSomething() -> Int {
        return 2
    }

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

3 ответов


где это задокументировано?

Как subscript:

Ссылка На Язык / Объявления / Объявление Индекса

вы можете перегрузить объявление индекса в типе, в котором оно объявлено, если параметры или тип возврата отличаются от того, который вы перегружаете.

Руководство По Языку / Индексы / Индекс Опции

класс или структура может предоставить столько реализаций индекса, сколько ему нужно, и соответствующий индекс, который будет использоваться, будет выведен на основе типы стоимости или значения, содержащиеся в фигурных скобках индекса в точке использования индекса.

Я не могу найти никаких официальных документов о перегрузке методов или функций. но в блоге Swift:

Переиначивая Все с быстрым REPL / переопределением или перегрузкой?

имейте в виду, что Swift позволяет перегрузить функцию, даже если две подписи отличаются только типом возврата.


тип функции определяется типом ее аргументов и типом ее возвращаемого значения, и компилятор может различать аналогично именованные функции по их типу - из вашего примера:

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()) для компилятора недостаточно информации, чтобы разобраться, какую функцию вы вызываете

редактировать редактировать - обратите внимание, что там, где я начинаю, говоря 'компилятор может различать одноименные функции по их типу', имена функций определяются: (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:)


Это довольно крутой аспект 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)