Consider this myFilter
function that takes in a generic argument and filters the array based on the predicate. This is same as the filter()
function provided by Swift.
func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] { var result = [T]() for i in source { if predicate(i) { result.append(i) } } return result }
How is this different from,
func myFilter(source: [AnyObject], predicate:(AnyObject) -> Bool) -> [AnyObject] { var result = [AnyObject]() for i in source { if predicate(i) { result.append(i) } } return result }
Aren't we achieving the point of generics even in the latter example?
Swift provides two special types for working with nonspecific types: Any can represent an instance of any type at all, including function types. AnyObject can represent an instance of any class type.
AnyObject can also be used as the concrete type for an instance of a type that bridges to an Objective-C class. Many value types in Swift bridge to Objective-C counterparts, like String and Int . The flexible behavior of the AnyObject protocol is similar to Objective-C's id type.
Swift Generics allows us to create a single function and class (or any other types) that can be used with different data types. This helps us to reuse our code.
Generics are type safe, meaning if you pass a string as a generic and try to use as a integer the compiler will complain and you will not be able to compile your (which is good). (This happens because Swift is using Static typing, and is able to give you a compiler error)
If you use AnyObject the compiler has no idea if the object can be treated as a String or as an Integer. It will allow you to do whatever you want with it (which is bad).
e.g. if you try to pass a String when it the your previously used Integer the application will crash. (This happens because Swift is using Dynamic typing and will only give you a runtime crash)
Generics basically tells the compiler:
"I am going to give you a type later and I want you to enforce that type everywhere I specify."
AnyObject basically tells the compiler:
"Don't worry about this variable no need to enforce any type here let me do whatever I want to."
Note: Icaro's answer would still be the accepted answer, I am just extending his explanation.
TL;DR : Check Icaro's answer.
About the usage of AnyObject
Icaro rightly puts:
Don't worry about this variable no need to enforce any type here let me do whatever I want to.
What does this mean? Let's take the code example in the question (I've gone a step up and changed AnyObject
to Any
without changing the meaning of the question):
func myFilter(source: [Any], predicate:(Any) -> Bool) -> [Any] { var result = [Any]() for i in source { if predicate(i) { result.append(i) } } return result }
This means, the myFilter
function takes in two arguments one source
and a closure predicate
. If you look closely, the contents of the source array can be ANYTHING. And the argument of the closure predicate
can be ANYTHING as well. If we were to name these "ANYTHING"s -- say ANYTHING1 and ANYTHING2 -- this approach doesn't require ANYTHING1 to be equal to ANYTHING2.
Let's sit back and ponder over the implications of this...
Say, we want to filter out evens from an array of integers and let's use our Any
filter for this
var ints = [1,2,3,4,5] as [Any] var predicate = { (a : Any) -> Bool in return (a as! Int) % 2 == 0 } let evens = myFilter(source: ints, predicate:predicate)
Wow, that worked, didn't it? All smiles? No.
Notice how in the line :
return (a as! Int) % 2 == 0
I'm forcefully down-casting a
. This line would crash if a
was anything other than an Int
. But its usage is justified; after all, we want to just filter out the Int
s and I am smart enough to use just an array of Int
s.
But, because say, I am a naive programmer, I do this :
var ints = [1,2,3,4,5,"6"] as [Any] var predicate = { (a : Any) -> Bool in return (a as! Int) % 2 == 0 } let evens = myFilter(source: ints, predicate:predicate)
This happily compiles, but crashes in the runtime. If only there was a way, where the compiler would tell me that this line...
var ints = [1,2,3,4,5,"6"]
... was faulty, we would not have had a crash. I would have fixed it right away!
Turns out, there is. Generics. To quote Icaro again,
I am going to give you a type later and I want you to enforce that type everywhere I specify.
func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] { var result = [T]() for i in source { if predicate(i) { result.append(i) } } return result } var ints = [1,2,3,4,5,6] var predicate = { (a : Int) -> Bool in return a % 2 == 0 } let evens = myFilter(source: ints, predicate:predicate)
This new filter is awesome. It won't let me do :
let evens = myFilter(source: ints, predicate:predicate)
because, the types of the predicate
and source
don't match. Compile time error.
Generics is generic in this way : in this specific example -- while at the point of writing the myFilter
function, you do not need to give a type of the source
or the argument that predicate
takes, it's T, it's ANYTHING. But once I say that source
is an array of ANYTHING, you HAVE to make sure the argument that the predicate
accepts is the same ANYTHING. With the background of our previous ANYTHING1, ANYTHING2 nomenclature, we can say that generics forces ANYTHING1 to be equal to ANYTHING2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With