Generics are by receiving ** type as a parameter. This is a function for writing general-purpose programs. ** **
In Swift, generics are provided as ** generic functions ** and ** generic types **.
In order to know the convenience of generics, here is an example of not using generics.
The following sample code I defined a function that confirms the equivalence of the two values received as arguments and returns the result.
func isEqual(_ x: Int, _ y: Int) -> Bool {
x == y
}
isEqual(1, 1) // true
This is the function when defined without using generics.
You can do general-purpose processing to compare arbitrary integers, ** This is only for integers of type Int. ** **
If you want to compare String type and Float type, You need to overload the same function for each type.
func isEqual(_ x: Int, _ y: Int) -> Bool {
x == y
}
func isEqual(_ x: String, _ y: String) -> Bool {
x == y
}
func isEqual(_ x: Float, _ y: Float) -> Bool {
x == y
}
isEqual(1, 1) // true
isEqual("abc", "abc") // true
isEqual(1.11, 1.12) // false
But with generics, ** General-purpose processing can be implemented even among multiple types. ** **
It looks like the following sample code.
<T: Equatable>
means
It means ** all types that comply with the Equatable protocol **.
String type, Int type, and Float type are all compliant with the Equatable protocol, so You can use this function.
Since the arguments are specified with the same T type like (_ x: T, _ y: T)
,
When passing arguments, you must pass the same type.
The fourth is when you pass a String type and an Int type.
func isEqual<T: Equatable>(_ x: T, _ y: T) -> Bool {
x == y
}
isEqual(1, 1) // true
isEqual("abc", "abc") // true
isEqual(1.11, 1.12) // false
isEqual("100", 100) //Compile error
Error details: Cannot convert value of type'Int' to expected argument type'String'
Unable to convert value of type'Int' to expected argument type'String'
To define generic functions and generic types It can be defined by adding ** type argument ** to the normal definition.
Enclose the type arguments in <>
, and if there are multiple types, separate them with , (comma)
.
//For normal functions
func function name(Argument name:Mold) ->Return type{
Processing executed when a function is called
}
//For generic functions
func function name<Type argument>(Argument name: Type argument) ->Return type{
Processing executed when a function is called
}
The type declared as a type argument is Inside a generic function or generic type ** it can be treated like a regular type. ** **
It can also be used as the return type of a generic function.
In the following sample code The first argument T contains the Int type, and the second argument U contains the String type. Also, the return type will be String type.
func sampleMethod<T, U>(_ x: T, _ y: U) -> U {
let a: T = x //Type annotation
let b = y //Type inference
let c = 1 as? T //Cast of type
print("The value of a is\(a), The type is\(type(of: a))\The value of nb is\(b), The type is\(type(of: b))\The type of nc is\(type(of: c))")
return y
}
sampleMethod(123, "abc")
Execution result
The value of a is 123 and the type is Int
The value of b is abc and the type is String
The type of c is Optional<Int>
Inside a generic function or generic type, you can abstractly represent the type as a type argument, If you want to actually call a generic function or instantiate a generic type, You must specify a specific value for the type argument.
In other words, what does that mean? When defining a function, type arguments can be used abstractly in various processes, Pass a specific value when calling a function or instantiating a type. about it.
** For what is generically defined using generics Establishing a type by giving a specific type argument is called specialization. **
There are two main methods of specialization.
In the following sample code ** One is to specify the type argument in <>, The other is type inference to infer type arguments. ** **
//Content is a type argument
struct Sample<T> {
let a: T
}
//When specifying that the type argument is of type String
let stringSample = Sample<String>(a: "abc") // Sample<String>
//When type inferring type arguments
let intSample = Sample(a: 123) // Sample<Int>
Optional \
For generics as well as functions, with the type arguments used when defining the generics If you want to explicitly distinguish the type arguments you specify when specializing generics,
** The former is called a formal argument and the latter is called a real argument. ** **
The good thing about generics is not just generalization ** It is generalized while maintaining type safety by static typing. ** **
What is static typing? It is a mechanism that the compiler checks the safety of the type before execution.
By being checked in advance Eliminates errors due to running type mismatch.
** Because type arguments are kept in generic functions and generic types The type given as a type argument has the same type safety as a normal type. ** **
In the following sample code It takes the type of the argument as the type argument T and returns the return value with the same type T.
In other words, if you pass an Int type as a type argument, the return type will be an Int type, If you pass a String type, the return type will be a String type.
It is guaranteed that the argument type and the return type, expressed as the same type T, are the same.
func sample<T>(_ x: T) -> T {
return x
}
let a = sample(123) //Int type
let b = sample("abc") //String type
Actually, it can be generalized not only for generics but also for Any type.
** Any type is a protocol that all types are implicitly compliant with. In other words, any type can be expressed. ** **
What is the difference between generics and Any type? When you say There is a big difference in type safety.
While generics are ** type generalized while maintaining type safety **, Any type is a ** type unsafe generalization **.
In the following sample code
Both sampleGeneric (_ :)
and sampleAny (_ :)
are generic.
So both take Int and String arguments The received value is returned as it is with return.
However, while the return type of a function that uses generics changes depending on the type argument, ** The return type of a function that uses the Any type will always be the Any type. ** **
What's wrong with this is ** Any type cannot be treated as the original type without downcasting, so When actually dealing with Any type variables and constants, it is necessary to downcast. ** **
//Functions using generics
func sampleGeneric<T>(_ x: T) -> T {
return x
}
let a = sampleGeneric(123) //Int type
let b = sampleGeneric("abc") //String type
//Function using Any
func sampleAny(_ x: Any) -> Any {
return x
}
let c = sampleAny(123) //Any type
let d = sampleAny("abc") //Any type
//Downcast as Int type
if let int = c as? Int {
//It can be treated as an Int type for the first time here
print("c is \(int)")
} else {
//It is necessary to consider the case where downcasting to Int type cannot be performed.
print("c is not Int")
}
Execution result
c is 123
That's the basic explanation of generics!
Any type and generics, Personally, I think it's a generic choice.
Any is better! I would be grateful if you could let me know if you have any questions.
Thank you for watching until the end.
Recommended Posts