定义结构:type 名字 类型
package main
import "fmt"
type Liters float64 //Liters就是float64类型
type Gallons float64 //Gallons就是float64类型
func main() {
var carFuel Gallons //定义一个Gallons类型的
var busFuel Liters //定义一个Liters类型的
//方法1:直接赋值
carFuel = 3.14
busFuel = 3.14
//方法2:转化
carFuel = Gallons(3.14)
busFuel = Liters(3.14)
fmt.Printf("carFuel: %v\n", carFuel) //carFuel: 3.14
fmt.Printf("busFuel: %v\n", busFuel) //busFuel: 3.14
}
短变量声明:
package main
import "fmt"
type Liters float64 //Liters就是float64类型
type Gallons float64 //Gallons就是float64类型
func main() {
carFuel := Gallons(3.14)
busFuel := Liters(3.14)
fmt.Printf("carFuel: %v\n", carFuel) //carFuel: 3.14
fmt.Printf("busFuel: %v\n", busFuel) //busFuel: 3.14
}
也可以这样写:
busFuel = Liters(Gallons(3.14))
有了这种写法,好处就是我们可以清晰知道一个数据的类型是什么类型,并且这个类型都有自己的含义
一个定义类型也可以使用基本运算符
package main
import "fmt"
type Liters float64 //Liters就是float64类型
type Gallons float64 //Gallons就是float64类型
type Title string
func main() {
carFuel := Gallons(3.14)
busFuel := Liters(3.14)
fmt.Println(carFuel + Gallons(busFuel)) //6.28
fmt.Println(carFuel - Gallons(busFuel)) //0
fmt.Println(carFuel * Gallons(busFuel)) //9.8596
fmt.Println(carFuel / Gallons(busFuel)) //1
fmt.Println(Title("aaa") == Title("aaa")) //true
fmt.Println(Title("aaa") > Title("bbb")) //false
fmt.Println(Title("aaa") < Title("bbb")) //true
fmt.Println(Title("aaa") + Title("aaa")) //aaaaaa
}
我们可以把转换的过程抽出来形成一个函数
package main
import "fmt"
type Liters float64 //Liters就是float64类型
type Gallons float64 //Gallons就是float64类型
type Title string
//Liters转化为Gallons
func ToGallons(l Liters) Gallons{
return Gallons(l * 0.264)
}
//Gallons转化为Liters
func ToLiters(g Gallons) Liters{
return Liters(g * 3.857)
}
func main() {
carFuel := Gallons(3.14)
busFuel := Liters(3.14)
fmt.Println(ToGallons(busFuel))
fmt.Println(ToLiters(carFuel))
}
这样好处就是看起来方便一点,而且意思比较明确,但是我们知道 Gallons 不只是 Liters 能转化。比如米可以由里面转换,但是也可以由分米进行,但是Go语言不支持重载的操作,所以我们只能选择使用修改函数名的方法,但是如果由很多个单位转换,那我们就要设置很多个方法。接下来就来看看另一种解决方法:使用方法修复函数名冲突问题
方法定义和函数很类似,事实上,它们只有一点不同:你需要增加一个额外的参数,一个接收器参数,它在函数名称之前的括号中。下面例子中 sayHi 定义在了 MyType上面
package main
import "fmt"
type MyType string //定义一个新的类型
//方法, 参数类型定义在函数名之前
func (m MyType) sayHi() {
//输出接收器参数的值
fmt.Println("Hi from", m)
}
func main() {
value := MyType("Go")
value.sayHi() //Hi from Go
}
按照惯例,Go开发者通常使用一个字母作为名称 - 小写的接收器类型名称的首字母,比如MyType首写字母小写就是m。
那么能不能为所有类型定义新的方法?方法和类型定义必须同一个包,所以说那些不同包的自定义类型或者int等基本数据类型是不可以定义方法的
事实上除了在接收器上被调用之外,方法和函数完全相同。就像函数一样,你也可以设置参数和返回值,并且方法名大写就是
package main
import "fmt"
type MyType string //定义一个新的类型
//方法
func (m MyType) SayHello(name string, content string) string {
fmt.Println("name: ", name, "content: ", content)
fmt.Println("接收器参数: ", m)
return "success"
}
func main() {
value := MyType("Go") //name: 张三 content: aaa
value.sayHello("张三", "aaa") //接收器参数: Go
}
这样看来方法就是和自定义类型进行绑定操作的了。
如果我们通过方法进行改变原参数,能不能呢?
package main
import "fmt"
type Number int
func (n Number) Double(){
n *= 2
}
func main() {
value := Number(4)
value.Double() //尝试加倍
fmt.Printf("value: %v\n", value) //value: 4
}
可以看到只是传入基本数据类型,那么结果还是不能加倍,这时候就可以使用指针了。
package main
import "fmt"
type Number int
func (n *Number) Double(){
*n *= 2
}
func main() {
value := Number(4)
value.Double() //尝试加倍
fmt.Printf("value: %v\n", value) //value: 8
}
下面这个例子也说明了,指针类型的参数也可以直接调用方法,Go会帮我们自动转化的,但是不建议这么使用,为了一致性,你所有的类型函数接受值类型或者都接收指针类型,应该避免混用的情况
package main
type Number int
func (n *Number) Double(){
*n *= 2
}
func (n Number) Double2(){
n *= 2
}
func main() {
value := Number(4)
pointValue := &value
value.Double() //尝试加倍
value.Double2() //尝试加倍
pointValue.Double()
pointValue.Double2()
}
最后注意一点:为了调用需要接收器指针的方法,我们需要获取这个值类型的指针
&Number(4).Double2()
这种写法是错误的Number(4).Double()
这种也是错误的,必须先获取,再调用package main
import "fmt"
type M float64
type CM float64
type KM float64
func (c CM) toM() M {
return M(c / 100)
}
func (k KM) toM() M {
return M(k * 1000)
}
func main() {
m := CM(174)
km := KM(1)
height := m.toM()
distance := km.toM()
fmt.Printf("height: %v\n", height) //height: 1.74
fmt.Printf("distance: %v\n", distance) //distance: 1000
}