gpt4 book ai didi

swift - Swift实现通用功能

转载 作者:行者123 更新时间:2023-11-28 11:43:15 26 4
gpt4 key购买 nike

我是一名Java开发人员,我正试图用Swift编写与Java代码相同的解决方案。
在斯威夫特身上能做到吗?
Java示例:

public interface Converter<S,T> {
T convert(S in)
}

public class CarConverterToDTO implements Converter<Car, CarDTO> {
@Override
public CarDTO convert(Car in) {
.....
}
}

示例Swift:
protocol Converter {
func convert<IN, OUT>(in: IN) -> OUT
}

如何实施?
谢谢!!!

最佳答案

看似简单的问题实际上是一个相当大且令人不快的冰山一角…
我首先要给你一个可能是解决你问题的真正办法:

class Converter<Input, Output> {
func convert(_ input: Input) -> Output {
fatalError("subclass responsibility")
}
}

struct Car { }
struct CarDTO { }

class DTOCarConverter: Converter<Car, CarDTO> {

override func convert(_ input: Car) -> CarDTO {
return CarDTO()
}

}

上面,我已经将您的Java Converter接口转换为Swift class而不是Swift protocol。这可能是你想要的。
现在我来解释原因。
从Java到Swift的程序员可能认为Swift协议相当于Java接口。所以你可以这样写:
protocol Converter {
associatedtype Input
associatedtype Output

func convert(_ input: Input) -> Output
}

struct Car { }
struct CarDTO { }

class /* or struct */ DTOCarConverter: Converter {
func convert(_ input: Car) -> CarDTO {
return CarDTO()
}
}

好的,现在你可以创建一个转换器并转换一些东西:
let converter = DTOCarConverter()
let car = Car()
let dto = converter.convert(car)

但是只要你想写一个以 Converter为参数的函数,你就会遇到一个问题:
func useConverter(_ converter: Converter) { }
// ^
// error: protocol 'Converter' can only be used as a generic constraint because it has Self or associated type requirements

“好吧,你说,”你忘了类型参数!“但是没有,我没有。Swift不允许在协议名之后使用显式类型参数:
func useConverter(_ converter: Converter<Car, CarDTO>) { }
// ^ ~~~~~~~~~~~~~
// error: cannot specialize non-generic type 'Converter'

我不想知道你为什么不能这么做。只要接受Swift协议通常并不等同于Java接口。
通常,没有关联类型且没有提到 Self的Swift协议相当于一个非通用Java接口。但是带有相关类型(或提到 Self)的Swift协议实际上并不等同于任何Java构造。
在讨论这个问题时,我们经常使用缩写“PAT”,它代表“Protocol with Associated Types”(包括提到 Self的协议)。PAT不定义可以用作函数参数、返回值或属性值的类型。拍拍也没什么用:
您可以定义子目录。例如, Equatable是PAT,因为它将 ==运算符定义为接受两个 Self类型的参数。 HashableEquatable的子目录。
可以使用PAT作为类型约束。例如, Set是泛型类型。 Set的类型参数名为 ElementSet将其 Element限制为 Hashable
所以你不能写一个参数为纯 Converter的函数。但是,您可以编写一个函数,将 Converter的任何实现作为参数,方法是使该函数成为泛型:
func useConverter<MyConverter: Converter>(_ converter: MyConverter)
where MyConverter.Input == Car, MyConverter.Output == CarDTO
{ }

汇编得很好。但有时使您的函数成为泛型是不方便的。
还有另一个问题这不能解决。您可能需要一个容器,其中包含从 ConverterCar的各种 CarDTOs。也就是说,你可能想要这样的东西:
var converters: [Converter<Car, CarDTO>] = []
// ^ ~~~~~~~~~~~~~
// error: cannot specialize non-generic type 'Converter'

我们不能像使用 converters函数那样,通过使 useConverter成为泛型来解决这个问题。
你最终需要的是一个“类型删除包装”。注意,这里的“type erased”与Java的“type eraser”有不同的含义。事实上,这几乎与Java的类型擦除相反。让我解释一下。
如果您在Swift标准库中查找,您将找到名称以 Any开头的类型,如 AnyCollection。这些大多是“类型删除包装”拍。 AnyCollection符合 Collection(这是PAT),并包装符合 Collection的任何类型。例如:
var carArray = Array<Car>()
let carDictionary = Dictionary<String, Car>()
let carValues = carDictionary.values
// carValues has type Dictionary<String, Car>.Values, which is not an array but conforms to Collection

// This doesn't compile:
carArray = carValues
// ^~~~~~~~~
// error: cannot assign value of type 'Dictionary<String, Car>.Values' to type '[Car]'

// But we can wrap both carArray and carValues in AnyCollection:
var anyCars: AnyCollection<Car> = AnyCollection(carArray)
anyCars = AnyCollection(carValues)

注意,我们必须在 AnyCollection中显式地包装其他集合。包装不是自动的。
这就是为什么我说这几乎与Java的类型擦除相反:
Java保留泛型类型,但删除类型参数。源代码中的A java.util.ArrayList<Car>在运行时变为A java.util.ArrayList<_>,而A java.util.ArrayList<Truck>在运行时也变为A java.util.ArrayList<_>。在这两种情况下,我们都保留容器类型( ArrayList),但删除元素类型( CarTruck)。
Swift类型擦除包装器擦除泛型类型,但保留类型参数。我们把一个 Array<Car>变成一个 AnyCollection<Car>。我们还将a Dictionary<String, Car>.Values转换为a AnyCollection<Car>。在这两种情况下,我们都会丢失原始容器类型( ArrayDictionary.Values),但保留元素类型( Car)。
所以不管怎样,对于 Converter类型,在容器中存储 Converters的一个解决方案是编写一个 AnyConverter类型的擦除包装器。例如:
struct AnyConverter<Input, Output>: Converter {
init<Wrapped: Converter>(_ wrapped: Wrapped) where Wrapped.Input == Input, Wrapped.Output == Output {
self.convertFunction = { wrapped.convert($0) }
}

func convert(_ input: Input) -> Output { return convertFunction(input) }

private let convertFunction: (Input) -> Output
}

(有多种方法可以实现类型擦除包装器。这只是一种方式。)
然后可以在属性类型和函数参数中使用 AnyConverter,如下所示:
var converters: [AnyConverter<Car, CarDTO>] = [AnyConverter(converter)]

func useConverters(_ converters: [AnyConverter<Car, CarDTO>]) {
let car = Car()
for c in converters {
print("dto = \(c.convert(car))")
}
}

但现在你应该问:这有什么意义?如果我要使用类型擦除包装器,为什么还要费心把 Converter变成一个协议呢?为什么不直接用一个基类来定义接口,用子类来实现呢?或者在初始化时提供了一些闭包的结构(如上面的 AnyConverter示例)?
有时,没有一个很好的理由使用协议,更明智的做法是使用类层次结构或结构。因此,您应该仔细研究如何实现和使用 Converter类型,看看非协议方法是否更简单。如果是的话,试试我在这个答案的顶部所展示的设计:定义接口的基类,以及实现接口的子类。

关于swift - Swift实现通用功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53198177/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com