-
Notifications
You must be signed in to change notification settings - Fork 52
01. Week 1 Golang Basic 2
Each line of code we create to performs a specific task, and we combine these lines of code to carry out a desired result. Sometimes we desire to repeat the lines of code with different data, and in some actions our code becomes so long that keeping track of what each part does is hard. Here we use, Functions which serve as a system tools that keep your code spotless and orderly.
A function is a group of statements that exist within a program for the purpose of executing a specific task. In Golang, a function can be called multiple times within the package, and also from other packages if the functions are exported to other packages.
Here is the syntax for writing functions in Go:
The first line of a function provides information about what the function will accept as an input and what to expect as an output and this we called as the function signature.
func [function_name] (param1 type, param2 type...) (returned type1, returned type2...) {
//Function body
}
The func keyword signifies that this is the start point of a function. Next comes the name of the function. Then there is a set of brackets declares the expected variables (a list of parameters) for this function. After that there is closing bracket comes with an optional list of return types. The opening brace signifies the start of the function body, which is wrapped up by the closing bracket between them we write the logic for the function.
In this example, the add() function takes input of two integer numbers and returns a integer value with a name of total.
package main
import "fmt"
func main() {
fmt.Println(add(20, 30))
}
func add(x int, y int) int {
total := 0
total = x + y
return total
}
The types of input and return value must match with function signature. If we will modify the above program and pass some string value in argument then program will throw an exception "cannot use "test" (type string) as type int in argument to add".
package main
import "fmt"
func main() {
add(20, 30)
}
func add(x int, y int) {
total := 0
total = x + y
fmt.Println(total)
}
We can also name the return value by defining variables, here a variable total of integer type is defined in the function declaration for the value that the function returns.
package main
import "fmt"
func main() {
fmt.Println(add(20, 30))
}
func add(x int, y int) (total int) {
total = x + y
return total
}
Variadic function is a function in which we can pass an infinite number of arguments to a function instead of just one argument at a time.
To declare a variadic function, the type of the final parameter is preceded by an ellipsis, "...", which shows that the function may be called with any number of arguments of this type.
In below example we will are going to print s[0]
the first and s[3]
the forth, argument value passed to variadicExample() function.
package main
import "fmt"
func main() {
variadicExample("red", "blue", "green", "yellow")
}
func variadicExample(s ...string) {
fmt.Println(s[0])
fmt.Println(s[3])
}
Needs to be precise when running an empty function call, if the code inside of the function expecting an argument and absence of argument will generate an error "panic: run-time error: index out of range". In above example you have to pass at least 4 arguments.
The parameter s accepts an infinite number of arguments. The tree-dotted ellipsis tells the compiler that this string will accept, from zero to multiple values.
package main
import "fmt"
func main() {
variadicExample()
variadicExample("red", "blue")
variadicExample("red", "blue", "green")
variadicExample("red", "blue", "green", "yellow")
}
func variadicExample(s ...string) {
fmt.Println(s)
}
In the above example, we have called the function with single and multiple arguments; and without passing any arguments.
package main
import "fmt"
func main() {
fmt.Println(calculation("Rectangle", 20, 30))
fmt.Println(calculation("Square", 20))
}
func calculation(str string, y ...int) int {
area := 1
for _, val := range y {
if str == "Rectangle" {
area *= val
} else if str == "Square" {
area = val * val
}
}
return area
}
In the following example, the function signature accepts an arbitrary number of arguments of type slice.
package main
import (
"fmt"
"reflect"
)
func main() {
variadicExample(1, "red", true, 10.5, []string{"foo", "bar", "baz"},
map[string]int{"apple": 23, "tomato": 13})
}
func variadicExample(i ...interface{}) {
for _, v := range i {
fmt.Println(v, "--", reflect.ValueOf(v).Kind())
}
}
An array is a data structure that consists of a collection of elements of a single type. An array in Go is a fixed-length data type that contains a contiguous block of elements of the same type. This could be a built-in type such as integers and strings, or it can be a struct type.
There are four common ways of declaring arrays.
[length]element_type
The type definition of an array is composed of its length, enclosed within brackets, followed by the type of its stored elements.
For example:
var intArray [5]int
he name of this array is intArray and the type structure is a [5]int
. The type includes the length of the array(number of items it must contain). A string array with a length of 10 items is of type [10]string
. In this example, the array is automatically populated by compiler, which assigns to each of the 5 index positions, a default value 0(int array type); space(string array type).
The default values - set when we declare an array - can then be reassigned manually to whatever data we want to have, as long as this data is of the intended type:
For example:
intArray[0] = 10
index[0]
assigns a value 10 to the first element of the array intArray. Like other programming languages, indexing in array starts with zero.
element_type{comma-separated list of element values}
The literal value for an array is composed of the array type definition followed by a set of comma-separated values, enclosed in curly brackets.
For example:
var intArray = [5]int{10,20,30}
In the above example, element[0] contains 10, element[1] contains 20 and remaining elements consist 0.
When arrays are initialized using an array literal, we have the option of customizing the index positions of the elements initially assigned to the array.
You can provide values for specific elements as shown here:
var intArray = [5]int{0:10,2:30,4:50}
Index element 0,2 and 4 were assigned to values 10,30 and 50; remaining values will be 0.
We can also declare array inside of a function using short variable declaration := like in the below example:
intArray := [5]int{10, 20, 30, 40, 50}
Index element 0,1...4 so on were assigned to values 10,20...50
The length of an array may be omitted and replaced by ellipses during initialization. Capacity is determined based on the number of values initialized.
The following will assign type [5]int
to variable intArray:
intArray := [...]int{10, 20, 30, 40, 50}
Create integer array named intArray, with a length of 5. Using a for loop to iterate over the array, display the contents of the array.
package main
import "fmt"
func main() {
intArray := [5]int{10, 20, 30, 40, 50}
for i := 0; i < len(intArray); i++ {
fmt.Println(intArray[i])
}]
}
The values "Japan", "Australia", "Germany" are assigned to strArray1. strArray1 is then assigned to strArray2, which means that a copy of those values is created. The reassignment of the strArray1 to strArray3 from a value assignment to a reference assignment.
package main
import "fmt"
func main() {
strArray1 := [3]string{"Japan", "Australia", "Germany"}
fmt.Printf("strArray1: %v\n", strArray1)
strArray2 := strArray1 // data is passed by value (copied)
fmt.Printf("strArray2: %v\n", strArray2)
strArray1[0] = "Canada"
fmt.Printf("strArray1: %v\n", strArray1)
fmt.Printf("strArray2: %v\n", strArray2)
strArray3 := &strArray1
fmt.Printf("strArray3: %v\n", strArray3)
fmt.Printf("&strArray3: %v\n", &strArray3)
fmt.Printf("*strArray3: %v\n", *strArray3)
}
strArray3 is a pointer string array, which means strArray3 is a pointer to the memory address of strArray1. In order to dereference strArray3, we have to add the * operator as a prefix *strArray3
.
package main
import "fmt"
func main(){
var x[5] int // Array Declaration
x[0]=10 // Assign the values to specific Index
x[4]=20 // Assign Value to array index in any Order
x[1]=30
x[3]=40
x[2]=50
fmt.Println("Values of Array X: ",x)
// Array Declartion and Intialization to specific Index
y := [5]int{0:100,1:200,3:500}
fmt.Println("Values of Array Y: ",y)
// Array Declartion and Intialization
Country := [5]string{"US","UK","Australia","Russia","Brazil"}
fmt.Println("Values of Array Country: ",Country)
// Array Declartion without length and Intialization
Transport := [...]string{"Train","Bus","Plane","Car","Bike"}
fmt.Println("Values of Array Transport: ",Transport)
}
A slice is a flexible and extensible data structure to implement and manage collections of data. A slice is a segment of dynamic arrays that can grow and shrink as you see fit. Like arrays, slices are indexable and have a length. Unlike arrays they're flexible in terms of length because they have their own built-in function called append, which can grow a slice quickly with efficiency. You can also trim the size of a slice by slicing out a part of the hidden memory.
Slices helps us in indexing, iteration, and garbage collection optimizations because the hidden memory is allocated in adjoining blocks.
There are three common ways to declare slices.
make([]Type, length,capacity)
A slice can be initialized at runtime using the built-in function make
An example program that declares slices in golang:
package main
import "fmt"
func main() {
var intSlice1 = make([]int,10) // when length and capacity is same
var intSlice2 = make([]int,10,20) // when length and capacity is different
fmt.Printf("intSlice1 \tLen: %v \tCap: %v\n", len(intSlice1), cap(intSlice1))
fmt.Printf("intSlice1 \tLen: %v \tCap: %v\n", len(intSlice2), cap(intSlice2))
}
Creates an underlying array of type [10]int
Creates the slice value with length and capacity of 10
Third parameter that specifies the maximum capacity of the slice is optional.
package main
import "fmt"
func main() {
var intSlice1 = new([50]int)[0:10]
fmt.Printf("intSlice1 \tLen: %v \tCap: %v\n", len(intSlice1), cap(intSlice1))
}
In above declaration slice starts with a length of 10
, going from 0
to 50
.
Capacity is 50
so it can expand up-to 50 without requesting a new array.
[]slice_type{comma-separated list of element values}
The following code snippet illustrates slice variables initialized with composite literal values:
package main
import "fmt"
func main() {
var intSlice = []int{10,20,30,40}
var strSlice = []string{"India","Canada","Japan"}
fmt.Printf("intSlice \tLen: %v \tCap: %v\n", len(intSlice), cap(intSlice))
fmt.Println(intSlice)
fmt.Printf("strSlice \tLen: %v \tCap: %v\n", len(strSlice), cap(strSlice))
fmt.Println(strSlice)
}
The length and capacity are of 3 and 4 elements respectively.
Number of elements provided in the literal is not surrounded by a fixed size. This suggests that the literal can be as large as needed.
Append adds elements onto the end of a slice. If there's sufficient capacity in the underlying array, the element is placed after the last element and the length is incremented. However, if there is not sufficient capacity, a new array is created, all of the existing elements are copied over, the new element is added onto the end, and the new slice is returned.
The following code snippet enlarge a slice using the append Function:
package main
import "fmt"
func main() {
// Create a smaller slice
a := make([]int, 2, 5)
a[0] = 10
a[1] = 20
fmt.Println("Slice A:", a)
fmt.Printf("Length is %d Capacity is %d\n", len(a), cap(a))
// Create a bigger slice
a = append(a, 30, 40, 50, 60, 70, 80, 90)
fmt.Println("Slice A after appending data:", a)
fmt.Printf("Length is %d Capacity is %d\n", len(a), cap(a))
}
Using make()
, create a slice of type []int
and name it a
Declare a length of 2
and capacity of 5
elements.
Idiomatic way in Go is to use :=
opertor instead of var
when creating slice inside main
function.
7
elements added in slice using append function which increased the length to 9
and capacity to 12
.
Go's built-in copy function is used to copy data from one slice to another. copy takes two arguments: dst and src. All of the entries in src are copied into dst overwriting whatever is there. If the lengths of the two slices are not the same, the smaller of the two will be used.
The following code snippet enlarge a slice using the copy Function:
package main
import "fmt"
func main() {
// Create a smaller slice
a := []int{5, 6, 7}
fmt.Printf("[Slice:A] Length is %d Capacity is %d\n", len(a), cap(a))
// Create a bigger slice
b := make([]int, 5, 10)
copy(b, a) // copy function
fmt.Printf("[Slice:B] Length is %d Capacity is %d\n", len(b), cap(b))
fmt.Println("Slice B after copying:", b)
b[3] = 8
b[4] = 9
fmt.Println("Slice B after adding elements:", b)
}
Using make()
, create a slice of type []in
t and name it b
Declare a length of 5
and capacity of 10
elements.
copy funcation copies all elements for a
into b
The following code snippet illustrates various slicing tricks:
package main
import "fmt"
func main() {
var countries = []string{"india", "japan", "canada", "australia", "russia"}
fmt.Printf("Countries: %v\n", countries)
fmt.Printf(":2 %v\n", countries[:2])
fmt.Printf("1:3 %v\n", countries[1:3])
fmt.Printf("2: %v\n", countries[2:])
fmt.Printf("2:5 %v\n", countries[2:5])
fmt.Printf("0:3 %v\n", countries[0:3])
fmt.Printf("Last element: %v\n", countries[4])
fmt.Printf("Last element: %v\n", countries[len(countries)-1])
fmt.Printf("Last element: %v\n", countries[4:])
fmt.Printf("All elements: %v\n", countries[0:len(countries)])
fmt.Printf("Last two elements: %v\n", countries[3:len(countries)])
fmt.Printf("Last two elements: %v\n", countries[len(countries)-2:len(countries)])
fmt.Println(countries[:])
fmt.Println(countries[0:])
fmt.Println(countries[0:len(countries)])
}
Declare a slice with 5 elements.
Using square bracket we can create different slices of a slices
len
function used to calculate the length of slice.
This technique illustrates the usage of ranges to select elements of a slice.
package main
import "fmt"
func main() {
var oldStr = []string{"india", "japan", "canada", "australia", "russia"}
var strSlice []string
newStr := oldStr[0:3]
strSlice = append(strSlice, oldStr[:1]...)
fmt.Printf("newStr: %v\n", newStr)
fmt.Printf("strSlice: %v\n", strSlice)
fmt.Printf("oldStr length: %v\tcapacity: %v\n", len(oldStr), cap(oldStr))
fmt.Printf("newStr length: %v\tcapacity: %v\n", len(newStr), cap(newStr))
fmt.Printf("strSlice length: %v\tcapacity: %v\n", len(strSlice), cap(strSlice))
newStr[0] = "china"
fmt.Printf("newStr: %v\n", newStr)
fmt.Printf("oldStr: %v\n", oldStr)
newStr = append(newStr, "brazil")
fmt.Printf("newStr: %v\n", newStr)
fmt.Printf("oldStr: %v\n", oldStr)
oldStr = append(oldStr, "us")
newStr = append(newStr, "uk")
fmt.Printf("newStr: %v\n", newStr)
fmt.Printf("oldStr: %v\n", oldStr)
}
Declare a slice
with 5
elements.
Declare slice
newStr
assign first three values of oldStr
This technique illustrates the usage of triple-dot ...
ellipsis to append a slice.
package main
import "fmt"
func main() {
var slice1 = []string{"india", "japan", "canada"}
var slice2 = []string{"australia", "russia"}
slice2 = append(slice2, slice1...)
fmt.Printf("slice1: %v\n", slice1)
fmt.Printf("slice2: %v\n", slice2)
}
Declare two slices with 2
and 3
items.
Append, the first slice slice1
to the second slice slice2
This technique illustrates the usage of ellipsis with range to append a slice.
package main
import "fmt"
func main() {
var slice1 = []string{"india", "japan", "canada", "us", "uk", "italy", "germany"}
var slice2 = []string{"australia", "russia"}
slice2 = append(slice2, slice1[3:]...)
fmt.Printf("slice1: %v\n", slice1)
fmt.Printf("slice2: %v\n", slice2)
}
Declare two slices with 2 and 3 items.
Append, the first slice slice1
to the second slice slice2
A map is a data structure that provides you with an unordered collection of key/value pairs (maps are also sometimes called associative arrays in Php, hash tables in Java, or dictionaries in Python). Maps are used to look up a value by its associated key. You store values into the map based on a key. The strength of a map is its ability to retrieve data quickly based on the key. A key works like an index, pointing to the value you associate with that key.
A map is implemented using a hash table, which is providing faster lookups on the data element and you can easily retrieve a value by providing the key. maps are unordered collections, and there's no way to predict the order in which the key/value pairs will be returned. Every iteration over a map could return a different order.
make[Key-type] [Value-type]
The Key-type specifies the type of a value that will be used to index the stored elements(Value-type) of the map. Map keys can be of any type numeric, string, Boolean, pointers, arrays, struct, and interface.
package main
import "fmt"
var employee = map[string]int{}
func main() {
fmt.Println(employee)
}
Map employee created having string as key-type and int as value-type
The literal mapped values are specified using a colon-separated pair of key and value as shown in below example.
package main
import "fmt"
var employee = map[string]int{"Mark":10,"Sandy":20}
func main() {
fmt.Println(employee)
}
The type of each key and value pair must match that of the declared elements in the map.
A map
value can also be initialized using the make
function.
package main
import "fmt"
func main() {
var employee = make(map[string]int)
employee["Mark"] = 10
employee["Sandy"] = 20
fmt.Println(employee)
employeeList := make(map[string]int)
employeeList["Mark"] = 10
employeeList["Sandy"] = 20
fmt.Println(employeeList)
}
The make
function takes as argument the type of the map and it returns an initialized map.
The built-in len() function returns the number of elements in a map.
package main
import "fmt"
func main() {
var employee = make(map[string]int)
employee["Mark"] = 10
employee["Sandy"] = 20
// Empty Map
employeeList := make(map[string]int)
fmt.Println(len(employee)) // 2
fmt.Println(len(employeeList)) // 0
}
The len function will return zero for an uninitialized map.
The built-in delete function deletes an element from a given map associated with the provided key.
package main
import "fmt"
func main() {
var employee = make(map[string]int)
employee["Mark"] = 10
employee["Sandy"] = 20
employee["Rocky"] = 30
employee["Josef"] = 40
fmt.Println(employee)
delete(employee,"Mark")
fmt.Println(employee)
}
In above example delete
function used to delete first element from employee map by passing key Mark as second argument in delete function.
package main
import "fmt"
func main() {
var employee = map[string]int{"Mark":10,"Sandy":20}
fmt.Println(employee) // Initial Map
employee["Rocky"] = 30 // Add element
employee["Josef"] = 40
employee["Mark"] = 50 // Edit element
fmt.Println(employee)
}
2 elements added and 1 edited in employee map after initialization.
The for…range
loop statement can be used to fetch the index and element of a map.
package main
import "fmt"
func main() {
var employee = map[string]int{"Mark": 10, "Sandy": 20,
"Rocky": 30, "Rajiv": 40, "Kate": 50}
for key, element := range employee {
fmt.Println("Key:", key, "=>", "Element:", element)
}
}
Each iteration returns a key and its correlated element content.
Go have the ability to declare and create own data types by combining one or more types, including both built-in and user-defined types. The declaration of new data type is constructed to provide the compiler with size and representation information, similar like built-in data types.
Structs are the only way to create concrete user-defined types in Go. Struct types are declared by composing a fixed set of unique fields. Each field in a struct is declared with a known type, which could be a built-in type or another userdefined type.
The syntax of a struct is as follows:
type identifier struct{
field1 data_type
field2 data_type
field3 data_type
}
The struct
type is composed by defining the keyword struct
followed by a set of field declarations enclosed within curly brackets.
An example program that declares struct
in golang:
package main
import "fmt"
type rectangle struct {
length float64
breadth float64
color string
}
func main() {
fmt.Println(rectangle{10.5,25.10,"red"})
}
As an example, a struct
named rectangle
containing two fields of type float64
and a third field of type string
.
Different fields in the same struct can have different data types.
Data type for each field can be of any type int, float, string etc...
A struct uses a selector expression (or dot notation) to access the values stored in fields.
The following code snippet illustrates struct instantiating:
package main
import "fmt"
type rectangle struct {
length int
breadth int
color string
geometry struct{
area int
perimeter int
}
}
func main() {
var rect rectangle
rect.length = 10
rect.breadth= 20
rect.color = "Green"
rect.geometry.area = rect.length * rect.breadth
rect.geometry.perimeter = 2 * (rect.length + rect.breadth)
fmt.Println(rect)
fmt.Println("Area:\t", rect.geometry.area)
fmt.Println("Perimeter:", rect.geometry.perimeter)
}
In the above example, selectors dot can be used as a rect.geometry.perimeter
chain to fetch field values that are nested inside a struct.
The following code snippet illustrates struct instantiating using var and := :
package main
import "fmt"
type rectangle struct {
length int
breadth int
color string
}
func main() {
var rect1 = rectangle{10,20,"Green"}
fmt.Println(rect1)
var rect2 = rectangle{length:10,color:"Green"} // breadth value skipped
fmt.Println(rect2)
rect3 := rectangle{10,20,"Green"}
fmt.Println(rect3)
rect4 := rectangle{length:10,breadth:20,color:"Green"}
fmt.Println(rect4)
rect5 := rectangle{breadth:20,color:"Green"} // length value skipped
fmt.Println(rect5)
}
You can skip value when you are specified fields name.
The following code snippet illustrates struct instantiating using new keyword :
package main
import "fmt"
type rectangle struct {
length int
breadth int
color string
}
func main() {
rect1 := new(rectangle)
rect1.length = 10
rect1.breadth = 20
rect1.color = "Green"
fmt.Println(rect1)
var rect2 = new(rectangle)
rect2.length = 10
rect2.color = "Red"
fmt.Println(rect2) // breadth skipped
}
rect2
is a pointer to an instance of rectangle
The fields are initially assign as a default of data type then we can reassign the field by using dot operator rect2.length
.
The following code snippet illustrates struct instantiating using &
:
package main
import "fmt"
type rectangle struct {
length int
breadth int
color string
}
func main() {
var rect1 = &rectangle{10,20,"Green"} // Can't skip any value
fmt.Println(rect1)
var rect2 = &rectangle{}
rect2.length = 10
rect2.color = "Red"
fmt.Println(rect2) // breadth skipped
var rect3 = &rectangle{}
(*rect3).breadth = 10
(*rect3).color = "Blue"
fmt.Println(rect3) // length skipped
}
In the above example, when you instantiating like var rect1 = &rectangle{10,20,"Green"}
you have to pass all field values and as per there data type otherwise compiler will give error like too few values in struct initializer or cannot use XXXXXX (type string) as type int in field value.
The fields are initially assign as a default of data type then we can reassign the field by using dot operator rect2.length
.
The following code snippet illustrates struct that id tagged with JSON annotation which can be interpreted by Go's JSON encoder and decoder:
package main
import (
"fmt"
"encoding/json"
)
type Employee struct {
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
City string `json:"city"`
}
func main() {
json_string := `
{
"firstname": "Rocky",
"lastname": "Sting",
"city": "London"
}`
emp1 := new(Employee)
json.Unmarshal([]byte(json_string), emp1)
fmt.Println(emp1)
emp2 := new(Employee)
emp2.FirstName = "Ramesh"
emp2.LastName = "Soni"
emp2.City = "Mumbai"
jsonStr, _ := json.Marshal(emp2)
fmt.Printf("%s\n", jsonStr)
}
In the above example, the tags are represented as raw string values which are wrapped within a pair of ``.
Creating a Struct type using other struct types as the type for the fields of structs:
package main
import "fmt"
func main() {
type Salary struct {
Basic, HRA, TA float64
}
type Employee struct {
FirstName, LastName, Email string
Age int
MonthlySalary []Salary
}
e := Employee{
FirstName: "Mark",
LastName: "Jones",
Email: "[email protected]",
Age: 25,
MonthlySalary: []Salary{
Salary{
Basic: 15000.00,
HRA: 5000.00,
TA: 2000.00,
},
Salary{
Basic: 16000.00,
HRA: 5000.00,
TA: 2100.00,
},
Salary{
Basic: 17000.00,
HRA: 5000.00,
TA: 2200.00,
},
},
}
fmt.Println(e.FirstName, e.LastName)
fmt.Println(e.Age)
fmt.Println(e.Email)
fmt.Println(e.MonthlySalary[0])
fmt.Println(e.MonthlySalary[1])
fmt.Println(e.MonthlySalary[2])
}
The Employee struct has been expanded by adding a new field MonthlySalary, for which the type is specified as a slice of a struct named Salary. Using the MonthlySalary field, you can specify monthly salary for an employee.
The following code snippet illustrates an example of Struc Type with Method:
package main
import "fmt"
type Salary struct {
Basic, HRA, TA float64
}
type Employee struct {
FirstName, LastName, Email string
Age int
MonthlySalary []Salary
}
func (e Employee) EmpInfo() string {
fmt.Println(e.FirstName, e.LastName)
fmt.Println(e.Age)
fmt.Println(e.Email)
for _, info := range e.MonthlySalary {
fmt.Println("===================")
fmt.Println(info.Basic)
fmt.Println(info.HRA)
fmt.Println(info.TA)
}
return "----------------------"
}
func main() {
e := Employee{
FirstName: "Mark",
LastName: "Jones",
Email: "[email protected]",
Age: 25,
MonthlySalary: []Salary{
Salary{
Basic: 15000.00,
HRA: 5000.00,
TA: 2000.00,
},
Salary{
Basic: 16000.00,
HRA: 5000.00,
TA: 2100.00,
},
Salary{
Basic: 17000.00,
HRA: 5000.00,
TA: 2200.00,
},
},
}
fmt.Println(e.EmpInfo())
}
Go language type system allows you to add methods to struct types using a method receiver. The method receiver specifies which type has to associate a function as a method to that type.
Go doesn't have classic Object Oriented concept of classes and inheritance. Interfaces in Go provide a way to specify the behavior of an object: do like this and reuse it.
In Go programming language Interfaces are types that just defines a set of methods (the method set), but these methods do not contain code. This mthods is never implemented by the interface type directly. Also an interface cannot contain variables. Go's interface type provides lot of extensibility and composability for your Go applications. Interface types express generalizations or abstractions about the behaviors of other types.
Interfaces are particularly useful as software projects grow and become more complex. They allow us to hide the incidental details of implementation the basic concept of Oops.
An interface type is declared with the keyword interface.
An interface is declared in the format:
type Name interface {
Method1(param_list) return_type
Method2(param_list) return_type
…
}
where Name is an interface type.
type Information interface {
General()
Attributes()
Inventory()
}
The interface type Information
is a contract for creating various Product
types in a catalog.
The Information
interface provides three behaviors in its contract: General
,Attributes
and Inventory
.
When a userdefined type implements the set of methods declared by an interface type the values of the user-defined type can be assigned back to the values of the interface type. In this process the value of the user-defined type getting stores into the interface value.
type Product struct {
Name, Description string
Weight,Price float64
Stock int
}
func (prd Product) General() {
fmt.Printf("\n%s",prd.Name)
fmt.Printf("\n%s\n",prd.Description)
fmt.Println(prd.Price)
}
func (prd Product) Attributes(){
fmt.Println(prd.Weight)
}
A struct Product is declared with fields for holding its state and methods implemented based on the behaviors defined in the Information interface.Multiple types can implement the same interface. A type that implements an interface can also have other functions. A type can implement many interfaces.
Go allows you to take existing types and both extend and change their behavior. This capability is important for code reuse and for changing the behavior of an existing type to suit a new need. This is accomplished through type embedding. It works by taking an existing type and declaring that type within the declaration of a new struct type. The type that is embedded is then called an inner type of the new outer type.
type Mobile struct{
Product
DisplayFeatures []string
ProcessorFeatures []string
}
A struct Mobile is declared in which the type Product is embedded. The type Product is an implementation of the Information interface, the type Mobile is also an implementation of the Information interface. All fields and methods defined in the Type Product types are also available in the Mobile type. In addition to the embedded type of Product, the Mobile struct p
Since any user-defined type can implement any interface, method calls against an interface value are polymorphic in nature. The userdefined type in this relationship is often called a concrete type, since interface values have no concrete behavior without the implementation of the stored user-defined value.
func (mob Mobile) Attributes(){
mob.Product.Attributes()
fmt.Println("\nDisplay Features:")
for _, key := range mob.DisplayFeatures{
fmt.Println(key)
}
fmt.Println("\nProcessor Features:")
for _, key := range mob.ProcessorFeatures{
fmt.Println(key)
}
}
The Mobile struct is a concrete implementation of the Information interface than an Product type. The Product type is defined for type embedding for making a more concrete implementation of the Information interface, like the Mobile struct. At this moment, the Mobile struct uses the methods that were defined in the Product struct. Because the Mobile struct is more of a concrete implementation, it might have its own implementations for its methods. Here the Mobile struct might need to override the methods defined in the Product struct to provide extra functionalities.
ⓒ 2019 Phú, Trần Phong & NordicCoder