Im trying to typecast an object to a class that uses Generics. Here is some code for better understanding
I've a protocol named wheel
protocol Wheel
I've a class named Wings
class Wings {
var count = 2
}
Now, I have a generic class named VehicleWrapper
class VehicleWrapper<T: Wings&Wheel> {
var vehicle: T
}
Now finally I have an object which I would want to typecast to VehicleWrapper
and use the count
property from Wings
class but I dont know the type T
would be while typecasting this. Is there a way to typecast this and use the count
variable?
CodePudding user response:
One problem with your question is that your code is illegal. You can't just say protocol Wheel
like that; you need curly braces (which may or may not contains the protocol's requirements). And your VehicleWrapper has no initializer, so the compiler will never allow it.
But let's suppose we've taken care of all that. My guess, then, is that the problem you're having is that it is not permitted to cast to a generic. For example, you cannot cast to a VehicleWrapper. This is because a generic is not a type. The type is the resolved generic.
To illustrate:
protocol Wheel {}
class Wings {
var count = 2
}
class VehicleWrapper<T: Wings & Wheel> {
var vehicle: T
init(vehicle: T) { self.vehicle = vehicle }
}
class Thing: Wings, Wheel {}
let thing = Thing()
class What<T: Wings & Wheel>: VehicleWrapper<T> {}
let what = What(vehicle: thing)
if let what = what as? VehicleWrapper { // compile error
print(what.vehicle.count)
}
As you can see, our attempt cast to a VehicleWrapper is met with scorn from the compiler. We could legally, however, try casting to a VehicleWrapper<Thing>
.
The real issue for your question is that it is difficult to imagine a use case where it make sense to need to do that, since how could this object come into existence in the first place without your knowing what it is?
CodePudding user response:
It isn't clear what you are trying to achieve, but I don't think that generics are the way to achieve it.
Generics essentially allow you to define operations on types, independent of what those types are. This is different to inheritance or protocols that allow you define the operations that can be performed on a particular type.
Most importantly, different generic object types are not co-variant; There is no functional relationship between GenericClass<SuperClass>
and GenericClass<SubclassOfSuperClass>
even though the generic types do have a inheritance relationship.
Taking your example, you are probably better off using some protocols.
Consider
protocol Wheeled {
var numberOfWheels: Int { get }
}
protocol Movable {
func moveForward()
func stop()
}
Now, we can define a Vehicle
class and some subclasses in terms of those protocols:
class Vehicle: Movable {
var name: String
var seatingCapacity: Int
init(name: String, seatingCapacity: Int) {
self.name = name
self.seatingCapacity = seatingCapacity
}
func moveForward() {}
func stop() {}
}
class Car: Vehicle, Wheeled {
var numberOfWheels: Int
init(name: String) {
self.numberOfWheels = 4
super.init(name: name, seatingCapacity: 5)
}
}
class Truck: Vehicle, Wheeled {
var numberOfWheels: Int
init(name: String) {
self.numberOfWheels = 18
super.init(name: name, seatingCapacity: 2)
}
}
Now, let's define a light aircraft:
protocol Winged {
var numberOfWings: Int { get }
func takeOff()
func land()
}
class LightAirplane: Vehicle, Wheeled, Winged {
var numberOfWheels: Int
var numberOfWings: Int
init(name: String) {
self.numberOfWheels = 3
self.numberOfWings = 2
super.init(name: name, seatingCapacity: 4)
}
func takeOff() {}
func land() {}
}
Using these definitions we can take an Vehicle
(whether it is a car, truck or plane) and ask it to moveForward()
or stop()
.
We can take an object that conforms to Winged
and ask it to takeOff()
and land()
.
Where could you use generics? Let's look at our Truck
- We can make that a generic class:
class CargoTruck<Cargo>: Truck {
private (set) var cargo: Cargo?
init(name: String, cargo: Cargo? = nil) {
self.cargo = cargo
super.init(name: name)
}
func load(cargo: Cargo) {
self.cargo = cargo
}
func unload() {
self.cargo = nil
}
}
Now we have a subclass of Truck
that can load and unload some sort of Cargo
but our implementation doesn't need to care what it is:
struct Cattle {}
struct Appliance {}
var cattleTruck = CargoTruck(name:"Cattle Truck", cargo:[Cattle]())
var applianceTruck = CargoTruck(name:"Container Truck", cargo: Appliance()))
We have cattleTruck
which is a CargoTruck<[Cattle]?
- i.e. it can hold an array of Cattle
and applianceTruck
which is a CargoTruck<Appliance>
- It can hold a single Appliance
What if we wanted to limit the type of the cargo - We can add a constraint to the generic type:
protocol ShippingContainer {
}
struct StandardContainer: ShippingContainer {
}
struct RefrigeratedContainer: ShippingContainer {
}
class ContainerTruck<Cargo: ShippingContainer>: CargoTruck<Cargo> {
}
let refer = ContainerTruck(name: "ReferTruck", cargo: RefrigeratedContainer())
refer.unload()
let bad = ContainerTruck(name:"Error", cargo: 12) // Error an Int is not a container
The generic doesn't define what the truck can do (move, load, unload etc), but rather what it does it to - It can load
a ShippingContainer