简体   繁体   English

通用工厂方法和类型推断

[英]Generic factory method and type inference

I have the following class with a generic factory method:我有以下带有通用工厂方法的 class:

final class Something<T> {
    
    let value: T
    
    init(initial: T) {
        value = initial
    }
    
}

extension Something {
    
    class func zip<A, B>(_ a: A, _ b: B) -> Something<(A, B)> {
        let initial = (a, b)
        return Something<(A, B)>(initial: initial)
    }
    
}

How come I can't call zip without explicitly specifying the return type?为什么我不能在没有明确指定返回类型的情况下调用zip

// ERROR: Cannot invoke `zip` with an argument list of type `(Int, Int)`
let y = Something.zip(1, 2)

// OK: Works but it’s unacceptable to require this on caller's side
let x = Something<(Int, Int)>.zip(1, 2)

Thank you for your time!感谢您的时间!

The reason you're seeing this is that there's nothing in this call: 你看到这个的原因是这个电话中没有任何内容:

let y = Something.zip(1, 2)

That tells Swift what T should be. 这告诉Swift T应该是什么。

Your call implicitly specifies what A and B should be, and specifies the method should return Something<A, B> . 您的调用隐式指定AB应该是什么,并指定该方法应返回Something<A, B> But that Something<A, B> is not connected to Something<T> . 但那个Something<A, B>没有连接到Something<T>

In fact, nothing at all in your call is connected to T ; 事实上,你的电话中没有任何东西与T ; T is left unspecified, so it could be anything. T未指定,因此可能是任何东西。 I mean that literally—you can actually put (nearly) any random type in the angle brackets after Something and it'll work exactly the same: 我的意思是字面意思 - 你实际上可以在Something之后将(几乎)任意随机类型放在尖括号中并且它将完全相同:

let y = Something<UICollectionViewDelegateFlowLayout>.zip(1, 2)

What you would really like to do is somehow specify that T has to be a tuple and the two parameters are of the same types as the tuple's elements. 你真正想做的是以某种方式指定T必须是一个元组,并且这两个参数与元组的元素具有相同的类型。 Unfortunately, Swift doesn't currently have the features needed to properly do that. 不幸的是,Swift目前还没有正确执行此操作所需的功能。 If the language were more sophisticated, you could say something like this: 如果语言更复杂,你可以这样说:

extension<A, B> Something where T == (A, B) {
    class func zip(a: A, _ b: B) -> Something {
        let initial = (a, b)
        return Something(initial: initial)
    }
}

But for now, you'll have to make do with this horrible hack, which works by meaninglessly reusing the T type parameter so that it's no longer at loose ends: 但是现在,你将不得不处理这个可怕的黑客攻击,它通过毫无意义地重用T类型参数来工作,这样它就不再处于松散状态:

extension Something {
    class func zip<B>(a: T, _ b: B) -> Something<(T, B)> {
        let initial = (a, b)
        return Something<(T, B)>(initial: initial)
    }
}

In short explanation, you use generics not correct. 简而言之,您使用泛型不正确。 It's not realtime feature, it's precompile thing. 它不是实时功能,它是预编译的东西。 If you need to make abstract class from generic input values, see and do like this: 如果需要从泛型输入值中创建抽象类,请查看并执行以下操作:

class Abstract<T> {
    init(value: T) {
        print("inputed value: \(value)")
    }
}

class Something {
    class func zip<A, B>(value: A, value2: B) -> Abstract<(A, B)> {
        print("Something.zip", value, value2)

        return Abstract<(A, B)>(value: (value, value2))
    }
}

Something.zip(5, value2: 40) // "inputed value: (5, 40)"

T simply isn't related to A and B in that way and so can't be inferred. T就这样简单地与AB无关,因此无法推断。

Eg. 例如。

let z = Something<(String, String)>.zip(1, 2)
let z2 = Something<AnyObject>.zip(1, 2)

work just fine to return a Something<(Int, Int)> 工作得很好,可以返回Something<(Int, Int)>

You can introduce type inference for your case like this:您可以像这样为您的案例引入类型推断:

final class Something<T> {

    let value: T

    init(initial: T) {
        value = initial
    }

    class func zip<A, B>(_ a: A, _ b: B)  ->  Something<T> where T == (A, B) {
        let initial = (a, b)
        return Something<(A, B)>(initial: initial)
    }

}

let y = Something.zip(1, 2) //works

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM