不一样的go语言-不同的语法之type
前言
在go语言中,type用于类型定义(type definition)与类型别名(type alias)。这两者的差别从名字上已经可以初见端倪。
类型定义即定义新类型,是一个全新的类型,但可能与被定义类型存在一些关系,如类型转换,方法继承等。类型定义在各语言中有不同的体现,如Java是用class, interface等关键字作类型定义,在python中也是使用class关键字。只不过go为了简洁的原则,可以省些关键字,因而与类型别名重用了type关键字。
类型别名则是对被定义类型的别称,与其是相同的类型,只不过取了另外一个名字而已。类型别名是本文讨论的重点。这里有几个问题,一是为何go要引入类型别名,其有什么好处?为什么Java或者其他语言没有类型别名?
在go语言中,type有5种作用,罗列如下。但在本文中,只讲述类型定义与类型别名。
- 定义接口
- 定义结构体
- 类型定义
- 类型别名
- 类型查询
类型定义
package main
import "fmt"
func main() {
h := House{height: 1.0, width: 2.0}
fmt.Println(h.Height())
wh := WhiteHouse{height: 1.0, width: 2.0}
fmt.Println(wh.Area())
w := House(wh) //类型转换
fmt.Println(w.Width())
f1 := Factory{height: 1.0, width: 2.0}
f2 := Factory2{name: "test"}
fw := House(f1)
fmt.Println(fw)
fw2 := House(f2) //运行时这里会报类型转换错误
fmt.Println(fw2)
}
type House struct {
height float32
width float32
}
func (h *House) Height() float32 {
return h.height
}
func (h *House) Width() float32 {
return h.width
}
type WhiteHouse House //类型定义
func (h *WhiteHouse) Area() float32 {
return h.height * h.width
}
type Factory struct {
height float32
width float32
}
type Factory2 struct {
name string
}
WhiteHouse与House是两个不同的类型,方法Area只属于WhiteHouse。 WhiteHouse可以转换为House类型。但对于类型Factory与Factory2,House与WhiteHouse都只能与Factory进行类型转换(尽管这种类型转换在实践中基本不会出现),而与Factory2进行类型转换则会报错。个中原因在于struct类型的内存布局是按顺序存储的,因为只要类型拥有的属性一致,则进行类型转换不会报运行时错误。既然是顺序存储,那么适当合理地安排不同类型属性的出现顺序,可以达到优化内存使用的目的。比如将几个可以计算出占用内存和为8、16、32、64的属性放在一起,当然内存对齐的位数因不同平台和编译器而异。
类型定义通常还用在定义函数类型上,示例如下:
type Handler func(name string)
func process(h Handler) {
h("test")
}
类型别名
在go语言中,类型别名的定义示例如下:
package main
import "fmt"
func main() {
h := House{height: 1.0, width: 2.0}
fz := Fangzi{height: 1.0, width: 2.0}
fmt.Println(h.Area()) //House也有了Area方法
fmt.Println(fz.Area())
}
type House struct {
height float32
width float32
}
func (h *House) Height() float32 {
return h.height
}
func (h *House) Width() float32 {
return h.width
}
type Fangzi = House //类型别名
func (f *Fangzi) Area() float32 {
return f.height * f.width
}
在语法上,类型别名与类型定义的差别仅仅是一个=号。但从上述示例可知,对Fangzi的改变,同样会体现在House上。Fangzi添加了Area方法,同时House也获得了计算Area的能力。这一点与类型定义是明显不同的。
从这里可以看出,类型别名的一个好处在于可以为不喜欢的类型取一个漂亮的名字。比如,如果你更喜欢Human多于Person的话。
type Person struct {
name string
age int
sex string
}
type Human = Person
除此之外呢?对于库开发者来说,新旧版本包的迁移应是比较常见的事,特别是大版本升级的时候。那么为了保证新旧之间的兼容,可能就会用到类型别名的特性。比如旧包中的com.eventer/lib/AI,大版本升级时,需要变成com.eventer/lib2/AI。如果直接去掉lib/AI,则会造成使用这个库的项目,在引用新包时需要修改所有引用了旧包AI类型的代码。因而更好的作法是在移除com.eventer/lib/AI原有代码之后,加上一个类型别名的定义,即可完美解决这个问题。
package com.eventer/lib
import "com.eventer/lib2"
type AI = lib2.AI
还有吗?如果下面的示例也能被称作一种用途的话,那么这一种用法就是"导出未被导出的类型"。
type house struct {
name string
height float32
width float32
}
func (h *house) getName() string {
return h.name
}
func (h *house) GetArea() string {
return h.height * h.width
}
type House = house
但我认为这还是跟前文所述的类型别名的标准用法一般无异,实在看不出有什么不同,除了单词与原类型相同而已。否则根本没必要这样写代码,直接把house声明为House不是更直截了当?除非是用于取别名。
如果go有泛型,那么类型别名在简化代码,提高代码可读性方面,无疑将带来极大的好处。特别是对于java语言那种泛型的复杂度让人抓狂的语言,只可惜java没有语言层面的类型定义语法。虽然可以通过一些取巧的方式也能达到类似的效果,比如继承。比如下面的例子:
public class LongArrayList extends ArrayList<Long> {
}
上述代码定义一个Long类型的动态数组,相当于为泛型的ArrayList定义了一个新类型,在代码中可以直接使用。这是采用代码技巧突破在java中不能在顶级(top level)定义类型别名的限制,但 通过定义一个类来完成这件事情,是不是太重量级了。再比如:
class Test<I extends Integer> {
<L extends Long> void x(I i, L l) {
System.out.println(i.intValue() + ", " + l.longValue());
}
}
这是在类级别或方法级别定义类型别名。上述例子是通过给Integer、Long定义更短的名字——I和L。
反正这两种都只是语法形式上的变通,通常没人这样用。
而在C语言中,有同样的功能typedef关键字,比如:
typedef struct Name1 {
elemtype ElemName;
} Name2, Name3;
其中Name1为结构体名,同时它还有两个"外号":Name2,Name3,因为c没有对象的概念,所以很明显这里的typedef并没有go的type关键字的内容多,但他们底层的原理应是相同的,都是通过在在编译时期替换完成。
请关注公众号

不一样的go语言-不同的语法之type的更多相关文章
- C语言-01-基本语法
一.学前需知 开发工具 windows平台:Visual C++6.0等 mac平台:Xcode6.0等 以下文章内容皆是以Xcode6.0为开发工具,clang编译器. Xcode的一些常用快捷键 ...
- 关于JS脚本语言的基础语法
JS脚本语言的基础语法:输出语法 alert("警告!"); confirm("确定吗?"); prompt("请输入密码");为弱 ...
- Xamarin XAML语言教程基础语法篇大学霸
Xamarin XAML语言教程基础语法篇大学霸 前 言 Xamarin是一个跨平台开发框架.它可以用来开发iOS.Android.Windows Phone和Mac的应用程序.使用Xamarin框 ...
- Cocos2d-x 脚本语言Lua基本语法
Cocos2d-x 脚本语言Lua基本语法 前面一篇博客对Lua这门小巧的语言进行了简单的介绍.本篇博客来给大家略微讲一下Lua的语法.不会长篇累牍得把Lua的全部语法都讲一遍,这里通过下面几点来讲L ...
- GO语言的基本语法之变量,常量,条件语句,循环语句
GO语言的基本语法之变量,常量,条件语句,循环语句 作为慕课网得笔记自己看 定义变量: 使用var关键字 var a, b, C bool var s1, s2 string = "hell ...
- tn文本分析语言(二) 基本语法
tn是desert和tan共同开发的一种用于匹配,转写和抽取文本的语言.解释器使用Python实现,代码不超过1000行. 本文主要介绍tn的基本语法.高级内容可以参考其他篇章.使用这样的语法,是为了 ...
- (转)JavaScript二:JavaScript语言的基本语法要求
摘自:http://blog.csdn.net/erlian1992 要学习好JavaScript,首先我们要懂JavaScript语言的一些基本语法要求: 一,区分大小写 JavaScript语言区 ...
- javaweb学习总结七(XML语言作用、语法)
一:XML语言的概念以及作用 1:xml概念:extensible Markup language,可扩展行标记语言,因为html的语法比较混乱,不够严谨. 用html写的系统不好维护,所以w3c组织 ...
- C语言-01基础语法
1) 总结常见文件的拓展名 .c 是C语言源文件,在编写代码的时候创建 .o 是目标文件,在编译成功的时候产生 .out 是可执行文件,在链接成功的时候产生 2) 总结 ...
随机推荐
- tr 设置margin、padding无效
tr.td设置margin 无效 tr 设置padding无效.td设置padding有效
- Delphi基础必记-快捷键
快捷键: F12 代码窗口/窗体之间切换Ctrl + Shift + F 查找文件 Ctrl + Shift + G 为接口加入新的GUIDF4 运行到光标位置 F5 设置/取消断点 或用光标点击F7 ...
- C语言中malloc函数返回值是否需要类型强制转换问题
1. 在C语言中, 如果调用的函数没有函数原型, 则其返回值将默认为 int 型. 考虑调用malloc函数时忘记了 #include <stdlib.h>的情况 此时malloc函数返回 ...
- Linux mmc framework2:基本组件之mmc
1.前言 本文主要mmc组件的主要流程,在介绍的过程中,将详细说明和mmc相关的流程,涉及到其它组件的详细流程再在相关文章中说明. 2.主要数据结构和API TODO 3. 主要流程 3.1 mmc_ ...
- jdk8系列二、jdk8方法引用、重复注解、更好的类型推断、新增注解
一.方法引用 方法引用使得开发者可以直接引用现存的方法.Java类的构造方法或者实例对象.方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码. 方法 ...
- 转载:gc的概念,如果A和B对象循环引用,是否可以被GC?
原文:https://www.cnblogs.com/zhchoutai/p/6784929.html ①首先说一下,GC里边在JVM其中是使用的ROOT算法,ROOT算法,什么称作为ROOT呢,就是 ...
- Android:自定义Dialog
自定义Dialog:显示SeekBar 效果图: 步骤: //SettingActivity.java button4.setOnClickListener(new View.OnClickListe ...
- 浏览器桌面通知Notification实践
一言不合就上图: 最近常常在浏览器看到这样的消息推送,还有QQ.com的推送,现在我对这个不了解,不知道叫消息自动推送对不对,这个时chrome浏览器的截图,出现在右下角,其他浏览器的样式可能有些微差 ...
- 【linux】tcpdump抓包
tcpdump -i eth0 host 192.168.11.22 -w ./target.cap 上面指令,抓取eth0中与192.168.11.22相关的流量,保存到target.cap中
- python 全栈开发,Day118(django事务,闭包,客户管理,教学管理,权限应用)
昨日内容回顾 一.django事务 什么是事务 一系列将要发生或正在发生的连续操作. 作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行. 事务处理可以确保除非事务性单元内的所有操 ...