泛型&类型擦除

18 阅读2分钟

泛型(Generics)和类型擦除(Type Erasure)是 Swift 中处理类型和抽象化的两个不同概念。下面我将解释它们的区别,并通过一个简单的示例进行演示。

1. 泛型(Generics)

泛型允许你编写灵活且可重用的代码,可以与多种类型一起工作。通过泛型,你可以编写不依赖于特定类型的代码,而是在实际使用时指定具体的类型。

泛型示例:

// 定义一个泛型栈
struct Stack<T> {
    private var elements: [T] = []

    mutating func push(_ item: T) {
        elements.append(item)
    }

    func pop() -> T? {
        return elements.popLast()
    }
}

// 使用泛型栈
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
print(intStack.pop())  // 输出: Optional(2)

var stringStack = Stack<String>()
stringStack.push("Hello")
stringStack.push("World")
print(stringStack.pop())  // 输出: Optional("World")

在这个示例中,Stack 是一个泛型结构体,T 是一个占位符类型。它可以用于存储任何类型的元素,如 IntString

2. 类型擦除(Type Erasure)

类型擦除是一种技术,用于隐藏具体类型的实现,只暴露协议接口。这种技术允许你在需要处理多个具体类型时,使用一个统一的接口来处理它们,从而避免直接操作具体类型。

类型擦除示例:

// 定义一个协议
protocol Animal {
    func makeSound() -> String
}

// 定义具体类型
struct Dog: Animal {
    func makeSound() -> String {
        return "Bark"
    }
}

struct Cat: Animal {
    func makeSound() -> String {
        return "Meow"
    }
}

// 类型擦除实现
struct AnyAnimal: Animal {
    private let _makeSound: () -> String

    init<A: Animal>(_ animal: A) {
        _makeSound = animal.makeSound
    }

    func makeSound() -> String {
        return _makeSound()
    }
}

// 使用类型擦除
let animals: [AnyAnimal] = [AnyAnimal(Dog()), AnyAnimal(Cat())]
for animal in animals {
    print(animal.makeSound())  // 输出: Bark \n Meow
}

在这个示例中,AnyAnimal 是一个类型擦除包装类,它隐藏了具体的 Animal 实现,通过 _makeSound 闭包提供了一个统一的 makeSound 方法。这样,我们可以将不同的 Animal 类型(如 DogCat)统一处理为 AnyAnimal 类型。

区别总结

  • 泛型 是一种编写灵活且可重用代码的机制,通过占位符类型 T 来处理多种类型。例如,在 Stack<T> 中,T 可以是任何类型。

  • 类型擦除 是一种隐藏具体类型实现的技术,通过使用一个统一的协议接口来处理不同类型。例如,在 AnyAnimal 中,我们隐藏了具体的 Animal 实现,提供了一个统一的接口来处理不同的 Animal 类型。

使用场景

  • 泛型:当你需要编写能够处理多种类型的代码,并且希望在编译时指定具体类型时使用。例如,泛型栈 Stack<T> 可以处理任意类型的元素。

  • 类型擦除:当你需要将不同具体类型的对象统一为一个协议类型进行处理时使用。例如,通过 AnyAnimal 类型擦除不同的 Animal 实现,以统一处理不同的动物。

希望这些解释和示例能够帮助你理解泛型和类型擦除的区别以及它们的实际应用场景!