[英]Use Type Erasure return Generic Type in a function with Swift (Cannot convert return expression of type…)
我對 swift 中的 generics 有疑問。 讓我們公開我的代碼。
protocol FooProtocol {
associatedtype T
}
protocol Fooable { }
extension Int : Fooable { }
extension String: Fooable { }
class AnyFoo<T>: FooProtocol {
init<P: FooProtocol>(p: P) where P.T == T { }
}
class FooIntImpClass: FooProtocol {
typealias T = Int
}
class FooStringImpClass: FooProtocol {
typealias T = String
}
func createOne(isInt: Bool) -> AnyFoo<Fooable> {
if isInt {
let anyFoo = AnyFoo(p: FooIntImpClass())
return anyFoo
} else {
let anyFoo = AnyFoo(p: FooStringImpClass())
return anyFoo
}
}
func createTwo<F: Fooable>(isInt: Bool) -> AnyFoo<F> {
if isInt {
let anyFoo = AnyFoo(p: FooIntImpClass())
return anyFoo
} else {
let anyFoo = AnyFoo(p: FooStringImpClass())
return anyFoo
}
}
createOne
出現錯誤
無法將類型“AnyFoo”(又名“AnyFoo”)的返回表達式轉換為返回類型“AnyFoo”
createTwo
出錯
無法將類型“AnyFoo”(又名“AnyFoo”)的返回表達式轉換為返回類型“AnyFoo”
為什么會這樣。 我正在返回正確的值。
和createOne
和createTwo
有什么區別
編輯以回應對問題的編輯:
createTwo
不起作用,因為您有與我在原始答案中所說的相同的誤解。 createTwo
決定F
應該是String
或Int
,而不是“任何符合Fooable
的類型”。
對於createOne
,您還有另一個常見的誤解。 泛型類是不變的。 AnyFoo<String>
不是AnyFoo<Fooable>
的一種。 事實上,它們是完全不相關的類型! 有關更多詳細信息,請參見此處。
基本上,您嘗試做的事情違反了類型安全,並且您重新設計了 API 並選擇了另一種不同的方法。
原始答案(用於問題的初始修訂)
您似乎對 generics 有一個常見的誤解。 通用參數由調用者決定,而不是被調用者。
在createOne
中,您將返回anyFoo
,它的類型是AnyFoo<Int>
,而不是AnyFoo<P>
。 方法(被調用者)自己決定P
應該是Int
。 這不應該發生,因為調用者決定了泛型參數應該是什么。 如果被調用者是通用的,它必須能夠使用任何類型(在約束內)。 無論如何, P
在這里無論如何都不能是Int
,因為P: FooProtocol
。
您的createOne
方法根本不應該是通用的,因為它僅適用於Int
:
func createOne() -> AnyFoo<Int> {
let anyFoo = AnyFoo(p: FooImpClass())
return anyFoo
}
以下是您試圖實現的目標嗎? (使用 Xcode 11.4 編譯和測試)
func createOne() -> some FooProtocol {
let anyFoo = AnyFoo(p: FooImpClass())
return anyFoo
}
編輯我終於設法保留了你的where clause
:)
編輯仍然不確定你想做什么,我仍然同意@Sweeper,但我喜歡嚴重濫用 generics :):
protocol FooProtocol {
associatedtype T
init()
}
protocol Fooable { }
extension Int : Fooable { }
extension String: Fooable { }
class AnyFoo<T>: FooProtocol {
init<P: FooProtocol>(p: P) where P.T == T { }
init<T>(p: T.Type) { }
required init() { }
}
class FooIntImpClass: FooProtocol {
typealias T = Int
required init() { }
}
class FooStringImpClass: FooProtocol {
typealias T = String
required init() { }
}
func createOne<F: FooProtocol>(foo: F.Type) -> AnyFoo<F.T> {
let anyFoo = AnyFoo<F.T>(p: F.init())
return anyFoo
}
func createTwo<F: FooProtocol>(foo: F.Type) -> some FooProtocol {
let anyFoo = AnyFoo<F.T>(pk: F.T.self)
return anyFoo
}
編譯,但我不知道如何處理它。
編輯
是的,我真的不知道:
let one = createOne(foo: FooStringImpClass.self) // AnyFoo<String>
print(type(of: one).T) // "String\n"
let two = createTwo(foo: FooIntImpClass.self) // AnyFoo<Int>
print(type(of: two).T) // "Int\n"
那是你想要的嗎?
AnyFoo
class 上放置一個where
子句,而不是它的初始化程序。
我應該補充一點,你在初始化器上的 where 子句是錯誤的,就像Sweeper說的:
無論如何,P 在這里無論如何都不能是 Int,因為 P: FooProtocol.
以下代碼編譯:
protocol FooProtocol { associatedtype T } class AnyFoo<T>: FooProtocol where T: FooProtocol { init<P: FooProtocol>(p: P) { } } class FooImpClass: FooProtocol { typealias T = Int } func createOne<P: FooProtocol>() -> AnyFoo<P> { let anyFoo: AnyFoo<P> = AnyFoo(p: FooImpClass()) return anyFoo }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.