English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
O Swift oferece genéricos para escrever funções e tipos flexíveis e reutilizáveis.
A biblioteca padrão do Swift é construída com código genérico.
Os tipos de array e dicionário do Swift são conjuntos genéricos.
Você pode criar um array de Int, um array de String, ou até mesmo um array de qualquer outro tipo de dados do Swift.
O exemplo a seguir é uma função não genérica exchange usada para trocar dois valores Int:
// Definir uma função para trocar dois valores func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA } var \&numb1 = 100 var \&numb2 = 200 print("Dados antes da troca: \(numb1) e \(numb2)") swapTwoInts(&numb1, \&numb2) print("Dados após a troca: \(numb1) e \(numb2)")
O resultado de execução do programa acima é:
Dados antes da troca: 100 e 200 Dados após a troca: 200 e 100
Os exemplos acima só são válidos para troca de variáveis do tipo Int. Se você quiser trocar dois valores String ou Double, precisará escrever uma função correspondente, como swapTwoStrings(_:_:) e swapTwoDoubles(_:_:], conforme mostrado a seguir:
func swapTwoStrings(_ a: inout String, _ b: inout String) { let temporaryA = a a = b b = temporaryA } func swapTwoDoubles(_ a: inout Double, _ b: inout Double) { let temporaryA = a a = b b = temporaryA }
A partir do código acima, podemos ver que o código funcional é o mesmo, apenas os tipos são diferentes. Neste caso, podemos usar genéricos para evitar a repetição de código.
Os genéricos usam nomes de tipos placeholders (usamos a letra T aqui) para substituir nomes de tipos reais (por exemplo, Int, String ou Double).
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
swapTwoValues seguido pelo nome de tipo placeholder (T) e colocado entre chaves (<T>
)。Esses colchetes apontam para que o T seja um nome de tipo placeholder dentro da definição da função swapTwoValues(_:_:), então o Swift não busca um tipo real chamado T.
O seguinte exemplo é uma função genérica exchange usada para trocar valores Int e String:
// Definir uma função para trocar dois valores func swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA } var \&numb1 = 100 var \&numb2 = 200 print("Dados antes da troca: \(numb1) e \(numb2)") swapTwoValues(\&numb1, \&numb2) print("Dados após a troca: \(numb1) e \(numb2)") var \&str1 = \ var \&str2 = \ print("Dados antes da troca: \(str1) e \(str2)") swapTwoValues(\&str1, \&str2) print("Dados após a troca: \(str1) e \(str2)")
O resultado de execução do programa acima é:
Dados antes da troca: 100 e 200 Dados após a troca: 200 e 100 Dados antes da troca: A e B Dados após a troca: B e A
Swift permite que você defina seus próprios tipos genéricos.
Classe personalizada, estrutura e enumeração funcionam com qualquer tipo, como o uso de Array e Dictionary.
A seguir, vamos escrever um tipo de coleção genérica chamado Stack (pilha), que permite adicionar novos elementos apenas no extremo da coleção (chamado empilhar), e também pode remover elementos apenas do extremo (chamado desempilhar).
A análise da imagem, de esquerda para direita, é a seguinte:
Existem três valores na pilha.
O quarto valor é empilhado na parte superior da pilha.
Agora há quatro valores na pilha, o valor mais recente empilhado está na parte superior.
O valor mais recente da pilha é removido, ou chamado de desempilhar.
Depois de remover um valor, a pilha agora tem apenas três valores.
A seguir está um exemplo de pilha não genérica, usando pilha de tipo Int como exemplo:
struct IntStack { var items = [Int]() mutating func push(_ item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } }
Esta estrutura usa uma propriedade Array chamada items para armazenar valores. A Stack fornece dois métodos: push(_) e pop(), usados para empilhar valores e remover valores da pilha. Esses métodos são marcados como mutating, porque precisam modificar o array items da estrutura.
A estrutura IntStack acima pode ser usada apenas para tipos Int. No entanto, pode-se definir uma estrutura Stack genérica para poder manipular valores de qualquer tipo.
Aqui está a versão genérica do mesmo código:
struct Stack<Element> { var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } } var stackOfStrings = Stack<String>() print("Elemento de string inserido na pilha:") stackOfStrings.push("google") stackOfStrings.push("w3codebox) print(stackOfStrings.items); let deletetos = stackOfStrings.pop() print("Elemento removido: " + deletetos) var stackOfInts = Stack<Int>() print("Inserir elementos inteiros na pilha: ") stackOfInts.push(1) stackOfInts.push(2) print(stackOfInts.items);
O resultado da execução do exemplo é:
Inserindo elementos de string na pilha: ["google", "w3codebox"] Elemento removido: w3codebox Inserir elementos inteiros na pilha: [1, 2]
A pilha Stack é basicamente a mesma que a IntStack, o parâmetro de tipo de suporte Element substitui o tipo Int real.
No exemplo acima, o Element é usado como suporte em três lugares:
Criar items Propriedade, usar Element Um array vazio do tipo para inicializá-lo.
Especificar push(_:) O parâmetro único do método item O tipo deve ser Element Tipos.
Especificar pop() O tipo de retorno do método deve ser Element Tipos.
Quando você estende um tipo genérico (usando a palavra-chave extension), você não precisa fornecer uma lista de parâmetros de tipo na definição da extensão. É mais conveniente que a lista de parâmetros de tipo declarada na definição original do tipo possa ser usada na extensão, e os nomes dos parâmetros provenientes do tipo original serão usados como referências para os parâmetros de tipo na definição original.
A seguir, uma instância de extensão do tipo genérico Stack é adicionada, que inclui uma propriedade de leitura calculada chamada topItem, que retornará o elemento no topo da pilha sem removê-lo da pilha:}}
struct Stack<Element> { var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } } extension Stack { var topItem: Element? { return items.isEmpty ? nil : items[items.count - 1] } } var stackOfStrings = Stack<String>() print("Elemento de string inserido na pilha:") stackOfStrings.push("google") stackOfStrings.push("w3codebox) if let topItem = stackOfStrings.topItem { print("O elemento no topo da pilha é: \(topItem).") } print(stackOfStrings.items)
No exemplo, a propriedade topItem retornará um valor opcional do tipo Element. Quando a pilha está vazia, topItem retornará nil; quando a pilha não está vazia, topItem retornará o último elemento do array items.
O resultado de execução do programa acima é:
Inserindo elementos de string na pilha: O elemento no topo da pilha é: w3codebox. ["google", "w3codebox"]
Também podemos especificar tipos associados através da extensão de um tipo existente.
Por exemplo, o tipo Array do Swift já fornece o método append(_:], uma propriedade count e um índice que aceita valores do tipo Int para recuperar seus elementos. Essas três funcionalidades atendem aos requisitos do protocolo Container, então você pode simplesmente declarar que Array adota o protocolo para estender Array.
A seguir, crie uma extensão vazia:
extension Array: Container {}
A restrição de tipo especifica um tipo de parâmetro que deve herdar de uma classe específica ou seguir um protocolo específico ou uma combinação de protocolos.
Você pode escrever uma restrição de tipo após o nome de um parâmetro de tipo, separada por dois pontos, como parte da cadeia de parâmetros de tipo. A sintaxe básica dessa restrição de tipo aplicada a funções genéricas é mostrada a seguir (igual à sintaxe de tipos genéricos):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // Aqui está a parte do corpo da função genérica }
Essa função tem dois parâmetros de tipo. O primeiro parâmetro T, que tem a restrição de que T deve ser um subtipo da classe SomeClass; o segundo parâmetro U, que tem a restrição de que U deve conformar ao protocolo SomeProtocol.
// Função não genérica, busca o índice da string especificada no array func findIndex(ofString valueToFind: String, in array: [String]) -> Int? { for (index, value) in array.enumerated() { if value == valueToFind { // Retorna o valor do índice ao encontrar return index } } return nil } let strings = ["google", "weibo", "taobao", "w3codebox", "facebook"] if let foundIndex = findIndex(ofString: "w3codebox", in: strings) { print("w3O índice do codebox é (foundIndex)") }
O índice começa em 0.
O resultado de execução do programa acima é:
w3O índice do codebox é 3
No Swift, o keyword associatedtype é usado para definir tipos associados como exemplo.
Abaixo está uma definição de exemplo do protocolo Container, que define um tipo de associação ItemType.
O protocolo Container especifica apenas três funcionalidades que qualquer tipo que obedece ao protocolo Container deve fornecer. Os tipos que obedeem ao protocolo podem fornecer funcionalidades adicionais além dessas três condições.
// protocolo Container protocolo Container { associatedtype ItemType // adicionar um novo elemento ao contêiner mutating func append(_ item: ItemType) // obter o número de elementos no contêiner var count: Int { get } // recuperar cada elemento do contêiner através do valor de índice de tipo Int subscript(i: Int) -> ItemType { get } } // A estrutura Stack obedece ao protocolo Container struct Stack<Element>: Container { // parte original da implementação de Stack<Element> var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // implementação da parte do protocolo Container mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } } var tos = Stack<String>() tos.push("google") tos.push("w3codebox) tos.push("taobao") // Lista de elementos print(tos.items) // Número de elementos print( tos.count)
O resultado de execução do programa acima é:
["google", "w3codebox, "taobao"] 3
As restrições de tipo garantem que o tipo esteja em conformidade com as restrições de definição de função ou classe genérica.
Você pode definir restrições para os parâmetros através da cláusula where na lista de parâmetros.
Você pode escrever uma cláusula where, imediatamente após a lista de parâmetros de tipo, seguida de uma ou mais restrições para o tipo de associação, e (ou) uma ou mais relações de equivalência (igualdade) entre o tipo e a associação.
abaixo há um exemplo de definição de uma função genérica chamada allItemsMatch, usada para verificar se dois exemplos de Container contêm elementos da mesma ordem e idênticos.
se todos os elementos puderem coincidir, retornar true, caso contrário, retornar false.
// protocolo Container protocolo Container { associatedtype ItemType // adicionar um novo elemento ao contêiner mutating func append(_ item: ItemType) // obter o número de elementos no contêiner var count: Int { get } // recuperar cada elemento do contêiner através do valor de índice de tipo Int subscript(i: Int) -> ItemType { get } } // // tipo genérico TOS que segue o protocolo Container struct Stack<Element>: Container { // parte original da implementação de Stack<Element> var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // implementação da parte do protocolo Container mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } } // extensão, usar Array como Container extension Array: Container {} func allItemsMatch<C>1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable { // verificar se dois contêineres contêm o mesmo número de elementos se someContainer.count != anotherContainer.count { return false } // Verificar se cada par de elementos coincide for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // Todos os elementos coincidem, retorna true return true } var tos = Stack<String>() tos.push("google") tos.push("w3codebox) tos.push("taobao") var aos = ["google", "w3codebox, "taobao"] if allItemsMatch(tos, aos) { print("Coincidir com todos os elementos") } else { print("Elemento não coincide") }
O resultado de execução do programa acima é:
Coincidir com todos os elementos