golang struct详解
type Pet interface { Name() string Category() string } type Dog struct { name string // 名字。 } func (dog *Dog) SetName(name string) { //注意这里定义的是指针方法 dog.name = name } func (dog Dog) Name() string { return dog.name } func (dog Dog) Category() string { return "dog" }
dog := Dog{"little pig"} fmt.Printf("The dog's name is %q.\n", dog.Name()) var pet Pet = dog //这里赋值只是复制了一个副本而已 除非赋值指针 dog.SetName("monster") fmt.Printf("The dog's name is %q.\n", dog.Name()) fmt.Printf("This pet is a %s, the name is %q.\n", pet.Category(), pet.Name()) fmt.Println()
我们看下结果
The dog's name is "little pig".
The dog's name is "monster".
This pet is a dog, the name is "little pig". //这里没有什么变化
为什么dog的name字段值变了,而pet的却没有呢?这里有一条通用的规则需要你知晓:如果我们使用一个变量给另外一个变量赋值,那么真正赋给后者的,并不是前者持有的那个值,而是该值的一个副本
再看下一个示列
dog1 := Dog{"little pig"} fmt.Printf("The name of first dog is %q.\n", dog1.Name()) //little pig dog2 := dog1 fmt.Printf("The name of second dog is %q.\n", dog2.Name()) //little pig dog1.name = "monster" fmt.Printf("The name of first dog is %q.\n", dog1.Name()) //monster fmt.Printf("The name of second dog is %q.\n", dog2.Name()) //little pig fmt.Println()
返回结果: 发现dog1赋值给dog2还是复制一个副本 结构不是引用类型
The name of first dog is "little pig".
The name of second dog is "little pig".
The name of first dog is "monster".
The name of second dog is "little pig".
// 示例3。 dog = Dog{"little pig"} fmt.Printf("The dog's name is %q.\n", dog.Name()) pet = &dog //这里注意 赋值了指针地址 dog.SetName("monster") fmt.Printf("The dog's name is %q.\n", dog.Name()) fmt.Printf("This pet is a %s, the name is %q.\n", pet.Category(), pet.Name()) //monster
结果自己演示下就知道了
我们再来看下指针
一般指针的空值就是nil 我们来看个示列
// 示例1。 var dog1 *Dog fmt.Println("The first dog is nil.") dog2 := dog1 if dog2 == nil{ fmt.Println("dog2 is nil") //这里会被打印 }else { fmt.Println("The second dog is nil.") }
我们再来看个示列 我们把dog值赋值给Pet接口(因为dog实现了Pet方法)
var pet Pet = dog2 if pet == nil { fmt.Println("The pet is nil.") } else { fmt.Println("The pet is not nil.") } fmt.Printf("The type of pet is %T.\n", pet) fmt.Printf("The value of pet is %v.\n", pet) fmt.Printf("The type of pet is %s.\n", reflect.TypeOf(pet).String()) fmt.Printf("The type of second dog is %T.\n", dog2) fmt.Printf("The value of second dog is %v.\n", dog2) fmt.Println()
这里很误导人 大多人会觉得 pet变量是nil其实不是
接口只有在申明或被直接赋值nil的时候才是nil 其它不是nil 当我们给一个接口变量赋值的时候,该变量的动态类型会与它的动态值一起被存储在一个专用的数据结构中
pet虽然被包装的动态值是nil,但是pet的值却不会是nil,因为这个动态值只是pet值的一部分而已。
当我们把dog2的值赋给变量pet的时候,dog2的值会先被复制,不过由于在这里它的值是nil,所以就没必要复制了。然后,Go 语言会用我上面提到的那个专用数据结构iface的实例包装这个dog2的值的副本,这里是nil。虽然被包装的动态值是nil,但是pet的值却不会是nil,因为这个动态值只是pet值的一部分而已。顺便说一句,这时的pet的动态类型就存在了,是*Dog。我们可以通过fmt.Printf函数和占位符%T来验证这一点,另外reflect包的TypeOf函数也可以起到类似的作用。换个角度来看。我们把nil赋给了pet,但是pet的值却不是nil。这很奇怪对吗?其实不然。在 Go 语言中,我们把由字面量nil表示的值叫做无类型的nil。这是真正的nil,因为它的类型也是nil的。虽然dog2的值是真正的nil,但是当我们把这个变量赋给pet的时候,Go 语言会把它的类型和值放在一起考虑
也就是说,这时 Go 语言会识别出赋予pet的值是一个*Dog类型的nil。然后,Go 语言就会用一个iface的实例包装它,包装后的产物肯定就不是nil了。只要我们把一个有类型的nil赋给接口变量,那么这个变量的值就一定不会是那个真正的nil。因此,当我们使用判等符号==判断pet是否与字面量nil相等的时候,答案一定会是false。那么,怎样才能让一个接口变量的值真正为nil呢?要么只声明它但不做初始化,要么直接把字面量nil赋给它。
上面打印出来的结果是:
The pet is not nil.
The type of pet is *main.Dog.
The value of pet is <nil>.
The type of pet is *main.Dog.
The type of second dog is *main.Dog.
The value of second dog is <nil>.
原文地址:https://www.cnblogs.com/jackey2015/p/11726470.html
- WordPress 路径相关函数总结(二):主题路径相关函数
- ASP.NET 2.0 中 Web 事件
- Visual Studio 必备可视化插件推荐
- WordPress 路径相关函数总结(一):站点路径相关函数
- 在 ASP.NET 2.0 中,Global.asax 文件没有后置代码,如何将Globa.asax中的页面移到代码文件中
- 如何启用WSS 3的匿名用户访问
- 移除 WordPress 后台“外观-主题” 管理功能
- 移除 WordPress 后台插件管理的“编辑”与“停用”功能
- 区域设置 ID (LCID) 表
- jquery 表单事件
- Jexus以.NET(Phalanger)方式支持PHP的网站
- jquery鼠标事件
- Jexus 负载均衡
- 用.NET Framework 2.0创建 Form设计器[翻译]
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 在Angular应用里使用Redux
- Elasticsearch 日志配置详解
- Cypress系列(52)- fixture() 命令详解
- Docker学习笔记---Dockerfile
- Java面试高频知识点汇总 JVM专题
- Java面试高频知识点汇总 网络协议专题
- 「Mysql优化大师一」mysql服务性能剖析工具
- FPGA设计心得(12)如何正确使用 in_system_ibert ?
- Java面试高频知识点汇总 垃圾回收(GC)机制专题
- Celery-分布式任务队列学习笔记
- Java面试高频知识点汇总 数据库专题
- Java面试高频知识点总结 Redis
- Java面试高频知识点总结 Spring
- 杂谈小程序
- pytest封神之路第四步 内置和自定义marker