不一样的go语言-玩转语法之一
这段时间为俗事所累,疲以应付,落下了不少想法,错过了更新的日子。这个专题开始之际,已经准备了不下十几个主题,而在写作的过程中,又有新想法与主题涌现出来。未来预计想写写的内容主要包括:
- 玩转语法系列
- Context的来龙去脉
- 函数、闭包与范畴论的关系
- 线程与协程之争
- 通道的那些事
今天的玩转语法系列,先起个头,调子稍微低一点,主要想与大家分享一下go语言在实践中的优美之处。用不同的语言来解决相同的问题,最能直观感受语言的魅力。
先来看看一版java代码的例子(这里没有贬低java的意思,仅仅是举个例子,java是一门很实用的语言):
public class Tea {
//名称
private String name;
//奶
public boolean milk;
//珍珠
public boolean pearl;
//冰
public boolean ice;
public String toString() {
return name.concat(", ")
.concat("milk: ")
.concat(String.valueOf(milk))
.concat(", pearl: ")
.concat(String.valueOf(pearl))
.concat(", ice: ")
.concat(String.valueOf(ice));
}
}
public class HeyTeaMaker {
public Tea make(String name, boolean milk, boolean pearl, boolean ice) {
Tea tea = new Tea();
tea.name = name;
tea.milk = milk;
tea.pearl = pearl;
tea.ice = ice;
return tea;
}
}
public static void main(String[] args) {
HeyTeaMaker maker = new HeyTeaMaker();
Tea tea = maker.make("HeyTea", true, true, false);
System.out.println("tea: " + tea);
}
上面的代码如果不加以设计,直接以这样的方式编写,那么如果需要给茶增加一个属性,比如糖,带来的修改是灾难性的,不单Tea、HeyTeaMake要修改,所有使用的地方都要修改。当然花一番心思设计一下,比如使用建造者模式包装一下。老话说,拥抱变化,java也是可以很优美的。
在这个例子中,其实茶的配料都是可选,但因为java天生没有可选参数,参数亦没有默认值,因而对于这个例子恰巧是java的弱项。而使用go就简洁多了,比如:
package main
import "fmt"
func main() {
fmt.Println(NewTea("HeyTea", AddMilk(), AddPearl()))
}
type Tea struct {
Name string
Milk bool
Pearl bool
Ice bool
}
func NewTea(name string, option ... TeaOption) *Tea {
tea := &Tea{Name: name}
for _, o := range option {
o(tea)
}
return tea
}
func (t *Tea) String() string {
return fmt.Sprintf("%s, milk: %t, pearl: %t, ice: %t", t.Name, t.Milk, t.Pearl, t.Ice)
}
type TeaOption func(t *Tea);
func AddMilk() TeaOption {
return func(t *Tea) {
t.Milk = true
}
}
func AddPearl() TeaOption {
return func(t *Tea) {
t.Pearl = true
}
}
func AddIce() TeaOption {
return func(t *Tea) {
t.Ice = true
}
}
go其实也没有可选参数,参数也不允许有默认值,但因为函数与闭包的配合使用,使得go很轻松,很简洁地就解决了问题。即使要给茶增加一个属性糖,只需修改Tea,并且加一个AddSugar方法即可,调用的地方如果不需要糖,就不用修改。
java8已经引入了lamba与FunctionalInterface,其实完全依照go的实现,java也是可以写出类似的代码来。比如:
import java.util.function.Function;
public class Tea {
//名称
private String name;
//奶
public boolean milk;
//珍珠
public boolean pearl;
//冰
public boolean ice;
public String toString() {
return name.concat(", ")
.concat("milk: ")
.concat(String.valueOf(milk))
.concat(", pearl: ")
.concat(String.valueOf(pearl))
.concat(", ice: ")
.concat(String.valueOf(ice));
}
public static Function<Tea, Void> addMilk() {
return (Tea t)-> {
t.milk = true;
return null;
};
}
public static Function<Tea, Void> addPearl() {
return (Tea t)-> {
t.pearl = true;
return null;
};
}
public static Function<Tea, Void> addIce() {
return (Tea t)-> {
t.ice = true;
return null;
};
}
public static Tea newTea(String name, Function<Tea, Void> ...option) {
Tea tea = new Tea();
tea.name = name;
for (Function<Tea, Void> item : option) {
item.apply(tea);
}
return tea;
}
public static void main(String[] args) {
Tea tea = newTea("HeyTea", addIce(), addMilk(), addPearl());
System.out.println("tea: " + tea);
}
}
功能倒是实现了,但感觉看起来仍然没有go好,一种妙不可言,只可意会不可言传的感觉。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App