Reputation: 648
I am trying to parameterize the type of the class property employee
. It may be that generics is not the right tool to solve this problem, but wanted to see this problem could be solved using them, anyway.
Let's say an employee can be of type Clincal
or Administrative
.
class EmployeeStateController {
var employee: Employee<Type>?
}
What I am wanting to do is to be able to initialize the state controller without knowing the employee type. The code above will not compile because Type
is an unknown type. In any case, it would compile if I did this:
class EmployeeStateController<Type> {
var employee: Employee<Type>?
}
But I do not want this since I DO NOT know Type
when EmployeeStateController
is instantiated. That is, I want to be able to do this:
// view controller 1: does not know the employee type just yet.
let stateController = EmployeeStateController() // employee remains nil for now
// view controller 2: knows the employee type so it can assign the employee type.
stateController.employee = Employee<Clinical>(name: "SomeName")
Upvotes: 1
Views: 49
Reputation: 1475
The problem is that Type
is part of type definition of EmployeeStateController
. You can't initialize an object whose type you don't know.
One idea is to make Employee conform to some protocol.
protocol EmployeeProtocol {}
struct Employee<Type>: EmployeeProtocol { ... }
Then you can initialize your class with the protocol instead:
class EmployeeStateController {
var employee: EmployeeProtocol
}
You can expand on this idea using type erasure if you can make some assertions about Type
:
Make a protocol your Type will conform too, and constrain EmployeeProtocol
's Type
to this protocol.
protocol JobDescribable {
var jobDescription: String { get }
}
// a Self-constrained protocol that refines Type
protocol EmployeeProtcool {
associatedType Type: JobDescribable
var type: Type { get }
}
struct Employee<Type: JobDescribable>: EmployeeProtocol {
var type: Type
}
Create a type-erased Type (AnyJobDescribable) and a type-erased EmployeeProtocol object (AnyEmployee), which can wrap any object that conforms to EmployeeProtocol
.
// Type-erased object that will serve as Type for the type-erased AnyEmployee
struct AnyJobDescribable: JobDescribable {
let describable: JobDescribable
var jobDescription: String { describable.jobDescription }
}
// AnyEmployee can be initialized by any EmployeProtocol type, including Employee
struct AnyEmployee: EmployeeProtocol {
var type: AnyJobDescribable
private var employee: Any
init<SpecializedEmployee: EmployeeProtocol>(employee: SpecializedEmployee) {
self.employee = empyloee
type = AnyJobDescribable(describable: employee.type)
}
}
Using this single AnyEmployee
object, you can now create an EmployeeStateController
that doesn't have to know about the different Employee objects you want to use.
// Using AnyEmployee, EmployeeStateController can use any type of Employee
// (or EmployeeProtocol) without requiring a generic type in its definition
class EmployeeStateController {
var employee: AnyEmployee?
}
Depending on what you're trying to do, this might not be worth it. Using employee as a protocol without an associatedtype
(like in the first example) is much more straightforward.
Upvotes: 1
Reputation: 54706
You cannot do that. All types must be known at compile time and the type of a variable cannot change after the type was instantiated.
The closest you can get is to make Employee
a protocol and assign a concrete conforming type when you know the type.
protocol Employee {
var name: String { get }
init(name: String)
}
struct Clinical: Employee {
let name: String
init(name: String) {
self.name = name
}
}
class EmployeeStateController {
var employee: Employee?
}
// view controller 1: does not know the employee type just yet.
let stateController = EmployeeStateController() // employee remains nil for now
// view controller 2: knows the employee type so it can assign the employee type.
stateController.employee = Clinical(name: "SomeName")
Upvotes: 2