0

class Car and Truck are written here as an example but they could be unknown to the program at compile time.

there could be more kinds of cars not yet known

for instance there could be a special class called Ferrari, Lamborghini, that may come down the road not known to the system.

our objective is to program to interface, not to a specific implemenation

we need to do similar to the following

  1. create an instance var vehicle: IDrive = Vehicle()
  2. vehicle.drive()

we tried some techniques and couldn't get it working without casting to specific implementations, need an independent solution without a need to cast to a specific implementation.

Any lateral approach also welcome, maybe our approach is totally wrong but keeping in mind the constraints that function instantiateAndDrive has to have a protocol based parameter

Note to negative voters aka dummies: instead ask questions to clarify if it doesn't make sense to you or go get yourself a "Design Pattern Book for Idiots"

    public protocol IDrive {
        func drive()
    }

    public class Car: IDrive {
        public init() {}
        public func drive() {}
    }

    class Truck: IDrive {
        public init() {}
        public func drive() {}
    }

class Test { //our attempts
   func instantiateAndDrive(Vehicle:IDrive.Type) {
       var vehicle = Vehicle()
       vehicle.drive()

   }

    func instantiateAndDrive2<T:IDrive>(Vehicle: T) {
        var vehicle = Vehicle()
        vehicle.drive()
    }
}

var test = Test()
test.instantiateAndDrive(Car.self)

Edit - Attempt using class after AirSpeed Velocity's Answer

public protocol Drivable {
    init()
    func drive()
}

public class Car: Drivable {
    public required init() {}
    public func drive() { println("vroom") }
}

public class Truck: Drivable {
    public required init() {}

    public func drive() { println("brrrrrrm") }
}

class Test {
    func instantiateAndDrive(Vehicle:Drivable.Type) {
        var vehicle = Vehicle()
        vehicle.drive()
    }

    func instantiateAndDrive2<T:Drivable>(Vehicle: T) {
        ver vehicle = Vehicle()
        vehicle.drive()
    }
}
//var test = Test()
//test.instantiateAndDrive(Car.self)
//test.instantiateAndDrive(Truck.self)
user2727195
  • 7,122
  • 17
  • 70
  • 118

2 Answers2

2

To be able to create a generic type generically, you need to know that it supports an initializer. You can do this by adding an init() function to the protocol:

public protocol Drivable {
    // enable drivable things to be
    // created with a  default initializer
    init()

    // (you could instead require a type implement
    // an initializer that took parameters)

    func drive()
}

You can then write functions that create and operate on them generically:

struct Car: Drivable {
    init() { }
    func drive() { println("vroom") }
}

struct Truck: Drivable {
    init() { }
    func drive() { println("brrrrrrm") }
}

struct Test<D: Drivable> {
    func instantiateAndDrive() {
        // whatever D is, it implements init()
        let d = D()
        // and drive()
        d.drive()
    }
}

Test<Car>().instantiateAndDrive()
Test<Truck>().instantiateAndDrive()

Though with this approach, when you say, “class Car and Truck are written here as an example but they could be unknown to the program at compile time” – the above code means they don’t need to be known to the implementation of Test at compile time. But they do need to be known to the caller of test at compile time.

This approach is more useful when a genric function is returning a type. For example, ExtensibleCollectionType requires init() and so can be used like this:

func returnCollectionContainingOne<C: ExtensibleCollectionType where C.Generator.Element == Int>() -> C {

    // this is allowed because the ExtensibleCollectionType procol 
    // requires the type implement an init() that takes no parameters
    var result = C()

    // and it also defines an `append` function that allows you to do this:
    result.append(1)

    // note, the reason it was possible to give a "1" as the argument to
    // append was because of the "where C.Generator.Element == Int" part
    // of the generic placeholder constraint 

    return result
}

// now you can use returnCollectionContainingOne with arrays:
let a: [Int] = returnCollectionContainingOne()

// or with ContiguousArrays:
let b: ContiguousArray = returnCollectionContainingOne()
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • awesome guru... you understand stuff, can we make this work via classes? or only through structs? – user2727195 Dec 17 '14 at 18:06
  • I've edited my question with suggested approach but tried to use classes, can you please help me on that end too – user2727195 Dec 17 '14 at 18:16
  • Airspeed, thanks for your attempt, I did say yes to it but actually I'm not there yet but I learned something in the process... :) here's my another attempt, please do check http://stackoverflow.com/questions/27534812/instantiate-from-protocol-type-reference-dynamically-at-runtime – user2727195 Dec 17 '14 at 21:06
  • final question, this is what I actually need to finally accomplish, please share your thoughts on this question, http://stackoverflow.com/questions/27571465/generic-types-collection – user2727195 Dec 19 '14 at 18:16
1

You want to use Self. In a protocol, it means "the type that actually adopts me". In a method of a class, it means "the class in which this method is actually called".

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • we have done using .self but coming across problems that still have to cast to specific implementation which we don't want. please try in playground, and will appreciate if you can come with a working solution – user2727195 Dec 17 '14 at 17:00
  • `Self` is not `self`. – matt Dec 17 '14 at 17:01
  • hmmmm that's new for me, can you please help with the code or refer to some docs to have it solved – user2727195 Dec 17 '14 at 17:03