简体   繁体   中英

Typecasting to Generic class in Swift

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?

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?

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]? - ie 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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