Nathan Anderson
Nathan Anderson

Reputation: 21

Unable to instantiate a generic type that uses another generic as a parameter - Cannot invoke initializer for type

class Request {

}
class Response<T: Request> {
    let request: T
    required init(request: T) {
        self.request = request
    }
}
class Adapter {        
    static func MakeRequest<T: Request, N: Response<T>>(request: T) -> N {
        let response = N(request: request)
        return response
    }
}

Error: Cannot invoke initializer for type 'N' with an argument list of type '(request: T)'

I would like to create a response class that stores the request as a generic type. When I try to instantiate a response passing in a generic request it returns the error above.

Upvotes: 2

Views: 550

Answers (2)

justinpawela
justinpawela

Reputation: 1978

What you are trying to do does not make the most sense.

If all of your request and response objects are going to be subclasses of Request and Response, then you have no need for generics. You won't be able to define anything other than subclasses of Request or Response that will be usable in your Adapter. Just define everything like this:

class Request {
}
class Response {
    let request: Request
    required init(request: Request) {
        self.request = request
    }
}
class Adapter {
    static func MakeRequest(request: Request) -> Response {
        let response = Response(request: request)
        return response
    }
}

However, I suspect that what you really want is to define Request and Response as protocols. This will allow you to make any class or struct conform to the Request or Response protocols and thus usable in your Adapter. This is a great use for generics.

protocol Request {
}
protocol Response {
    var request: Request { get }
    init(request: Request)
}
class Adapter {
    static func MakeRequest<T: Request, N: Response>(request: T) -> N {
        let response = N(request: request)
        return response
    }
}

Edit

Based on your comment, I see that you want to use a subclass of Request in Response without having to typecast. Certainly generics could make that possible, but it will not be useful to you.

Imagine you have your Request and Response classes defined as you did:

class Request {
}
class Response<T: Request> {
    let request: T
    required init(request: T) {
        self.request = request
    }
}

And you have a special Request subclass defined as MyRequest, which contains an additional property:

class MyRequest: Request {
    let numberOfRetries: Int = 3
}

And you create a response containing a MyRequest:

let myRequest = MyRequest()
let aResponse = Response(request: myRequest)

This all works. But what can you do with it? In a playground, you can check numberOfRetries and see that it's working:

aResponse.request.numberOfRetries     // 3

But you can't write any code in a real app that will take advantage of this. Using your Adapter as an example, any Adapter instance cannot check numberOfRetries without being specialized, because a generic Adapter that accepts a generic Response which accepts a generic Request will not be able to assume that the request was a MyRequest instance, and thus cannot assume that numberOfRetries is present.

If you want your Adapter to be able to take advantage of a Request or Response subclass's functionality with typecasting or checking, you will need a specialized Adapter subclass with constraints on what Request and Response it handles. And if you are creating an Adapter subclass, I'd say the usefulness of generics in your situation is severely limited.

In other words, an Adapter instance (or any other code that deals with Response objects) will not be able to take advantage of a Response subclass's special functionality without typecasting, specifically because your use of generics means that the Adapter cannot assume what kind of Response it will be working with.

Upvotes: 1

Qbyte
Qbyte

Reputation: 13243

Given your question and your comment on @user2194039 it is probably the best way to instantiate it directly since you need to provide the type information:

// if the function would work in your example you have to call it like so
let response: URLResponse<String> = Adapter.MakeRequest("Hello")

// this would be the easiest way
let response = URLResponse("Hello")

If your example is more complex please provide more information.

Upvotes: 0

Related Questions