golang 基本api

206 阅读24分钟

Go语言中的关键字(keywords)是一些被编译器预定义并具有特殊含义的标识符,它们用于控制程序的结构、流程和行为。下面列出了Go语言中的关键字:

  1. break:用于中断当前循环或switch语句的执行。
  2. case:在switch语句中用于匹配某个值。
  3. chan:用于声明一个通道(channel)类型。
  4. const:用于声明常量。
  5. continue:用于继续下一次循环的执行。
  6. default:在switch语句中用于指定默认情况。
  7. defer:用于延迟执行一个函数调用,通常用于资源释放等场景。
  8. else:用于if语句的否定分支。
  9. fallthrough:在switch语句中用于强制执行下一个分支的代码块。
  10. for:用于循环语句。
  11. func:用于声明一个函数或方法。
  12. go:用于启动一个新的并发执行的函数。
  13. goto:用于无条件跳转到指定的代码标签处。
  14. if:用于条件语句。
  15. import:用于导入包。
  16. interface:用于声明接口类型。
  17. map:用于声明一个映射(map)类型。
  18. package:用于声明包。
  19. range:用于迭代数组、切片、通道或映射的元素。
  20. return:用于从函数中返回结果。
  21. select:用于选择多个通道操作的一个执行。
  22. struct:用于声明结构体类型。
  23. switch:用于多路分支选择。
  24. type:用于声明自定义类型。
  25. var:用于声明变量。

这些关键字在Go语言中具有特殊的含义,不能用作标识符或变量名。了解和熟悉这些关键字对于正确编写和理解Go语言程序非常重要。

数据类型对比

下面是Java和Go语言中的基本数据类型及数组的对比:

基本数据类型对比:

类型JavaGo
整数类型byte, short, int, longint, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64
浮点数类型float, doublefloat32, float64
字符类型charrune
布尔类型booleanbool

数组对比:

类型JavaGo
数组int[] arrvar arr []int
多维数组int[][] arrvar arr [][]int

注意事项:

  • Go语言的整数类型包括有符号和无符号整数类型,而Java语言中只有有符号整数类型。
  • Go语言的字符类型rune是一个32位的Unicode码点,而Java语言中的char是一个16位的Unicode码点。
  • Go语言的布尔类型是bool,只能取truefalse两个值;Java语言的布尔类型是boolean,同样只能取truefalse两个值。
  • 在Go语言中,数组的长度是类型的一部分,因此[]int[5]int是不同的类型,分别表示长度不确定和长度为5的整型数组;而在Java语言中,数组的长度是不包含在类型中的,因此int[]int[5]表示相同类型的整型数组。
  • Go语言中的数组是值类型,赋值和传递数组时会复制整个数组;而Java语言中的数组是引用类型,赋值和传递数组时会传递数组的引用。

Java

// 基本类型
int anInt = 10;
double aDouble = 10.5;
boolean aBoolean = true;
char aChar = 'A';

// 包装类型
Integer anInteger = 10;
Double aDoubleObj = 10.5;
Boolean aBooleanObj = true;
Character aCharObj = 'A';

// 字符串
String aString = "Hello, World!";

Go

// 基本类型
var anInt int = 10
var aDouble float64 = 10.5
var aBoolean bool = true
var aChar rune = 'A'

// Go 没有包装类型,基本类型本身支持很多操作

// 字符串
var aString string = "Hello, World!"

简化的声明语法来简化上述变量声明,例如:

anInt := 10
aDouble := 10.5
aBoolean := true
aChar := 'A'

aString := "Hello, World!"

常量对比

特征Go语言中的常量Java语言中的常量
定义使用 const 关键字定义使用 final 关键字定义
声明时赋值声明常量时必须进行赋值声明常量时必须进行赋值
值的类型可以是基本数据类型、字符串、枚举类型等可以是基本数据类型、字符串、枚举类型等
编译时确定性常量的值必须在编译时确定,不能包含运行时才能确定的值常量的值必须在编译时确定,不能包含运行时才能确定的值
使用可以直接使用常量名字可以直接使用常量名字
注意事项- 常量在声明时必须赋值,且一旦声明就不能修改。- 常量在声明时必须赋值,且一旦声明就不能修改。
- 常量的值必须是编译时可确定的表达式。- 常量的值必须是编译时可确定的表达式。

Go

在Go语言中,常量(constants)是指在编译时就确定并且不可更改的值。常量在程序运行时不会发生变化,通常用于定义不会改变的值,例如数学常数、枚举值等。

以下是如何使用常量的示例:

1. 定义常量:

const pi = 3.14159
const (
	monday = 1
	tuesday = 2
	wednesday = 3
)

2. 使用常量:

fmt.Println("Pi value:", pi)
fmt.Println("Monday:", monday)

3. 注意事项:

  • 常量在声明时必须赋值,且一旦声明就不能修改。
  • 常量可以是基本数据类型(如整数、浮点数、布尔值等)、字符串、或枚举类型(使用const关键字定义一组常量值)。
  • 常量的值必须是编译时可确定的表达式,不能包含运行时才能确定的值(如函数调用、变量赋值等)。

示例:

package main

import "fmt"

const (
	pi = 3.14159
	monday = 1
	tuesday = 2
	wednesday = 3
)

func main() {
	fmt.Println("Pi value:", pi)
	fmt.Println("Monday:", monday)
}

在这个示例中,我们定义了一个数学常数pi和一组星期几的枚举常量。然后在main函数中使用这些常量,并输出它们的值。

Java

在Java中,常量通常使用 final 关键字进行定义,可以分为两种类型:类常量和实例常量。

类常量:

类常量是属于类的,使用 static final 修饰,一旦赋值就不能修改,可以通过类名直接访问。

public class Constants {
    public static final double PI = 3.14159;
    public static final int MAX_SIZE = 100;
}

使用类常量:

double circleArea = Constants.PI * radius * radius;

实例常量:

实例常量是属于对象的,使用 final 修饰,在对象创建时赋值,之后不能修改。

public class Circle {
    public final double PI = 3.14159;
    public final int radius;

    public Circle(int radius) {
        this.radius = radius;
    }
}

使用实例常量:

Circle circle = new Circle(5);
double circleArea = circle.PI * circle.radius * circle.radius;

注意事项:

  • 常量在声明时必须赋值,且一旦声明就不能修改。
  • 常量的值必须是编译时可确定的表达式。
  • 类常量使用 static final 修饰,可以通过类名直接访问;实例常量使用 final 修饰,必须在对象创建时赋值。

defer

下表将详细对比Go语言中的defer和Java语言中的finally

特征Go语言中的deferJava语言中的finally
语法使用 defer 关键字使用 finally 关键字
执行时机在函数退出之前执行try 块或 catch 块的代码执行结束后执行
资源释放用于释放资源、清理操作等用于释放资源、确保代码执行完成后进行清理操作
使用方式可以在函数内的任何位置使用只能在 try 块或 catch 块的后面使用
延迟执行的内容可以延迟执行函数调用、方法调用、闭包等通常用于释放资源或确保代码执行完成后进行清理操作
控制流程执行顺序是后进先出(LIFO),最后声明的最先执行try 块或 catch 块的代码执行结束后执行
关联的异常处理不直接处理异常,仅在函数退出前执行用于处理异常,确保在异常发生时也能执行必要的清理操作
与返回语句的关系不会影响函数的返回会在 return 语句执行前执行

示例代码:

Go语言中的defer:

package main

import "fmt"

func main() {
    defer fmt.Println("world") // 将打印 "world" 延迟到函数返回之前执行
    fmt.Println("hello")
}

Java语言中的finally:

public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("hello");
        } finally {
            System.out.println("world"); // 在try块的代码执行结束后执行
        }
    }
}

在这两个示例中,deferfinally 都确保了在函数退出前执行某些操作,但是它们的使用方式、语法和执行时机有所不同。

集合容器对比

好的,以下是使用表格展示Java和Go语言中常用的集合容器的对比:

集合容器JavaGo
动态数组ArrayList切片(Slice)
双向链表LinkedList-
哈希表HashMap映射(Map)
哈希集合HashSet-
有序映射TreeMap-
有序集合TreeSet-
通道-通道(Channel)

在这个表格中,Java语言中的集合容器需要导入对应的包才能使用,而Go语言中的集合容器是内置类型,不需要导入特定的包即可使用。

Java

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

// 列表
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

// 集合
Set<String> set = new HashSet<>();
set.add("a");
set.add("b");

// 映射
Map<String, Integer> map = new HashMap<>();
map.put("key1", 1);
map.put("key2", 2);

Go

要使用 make 创建切片、集合(set)、映射(map),你可以按照以下方式使用 make 函数:

创建切片(Slice):

slice := make([]T, length, capacity)

例如,创建一个长度为 5,容量为 10 的整型切片:

slice := make([]int, 5, 10)

创建集合(Set):

Go语言中并没有内置的集合类型,但可以使用 map[T]bool 实现类似集合的功能,其中 T 是集合元素的类型,bool 表示元素是否存在于集合中。

set := make(map[T]bool)

例如,创建一个字符串集合:

set := make(map[string]bool)

创建映射(Map):

m := make(map[K]V)

其中 K 表示键的类型,V 表示值的类型。

例如,创建一个字符串到整数的映射:

m := make(map[string]int)

使用 make 函数创建切片、集合和映射时,可以指定初始的长度和容量。对于切片和映射,容量是可选的,如果省略容量参数,则会创建一个具有指定长度的切片或映射。

slice := make([]int, 5, 10)

这将创建一个包含 5 个整数的切片,初始值都为 0,并且底层数组的容量为 10。

// 切片(类似于列表)
list := []int{1, 2, 3}
list = append(list, 4)

// 集合(Go 没有内置集合类型,但可以用 map 实现)
set := make(map[string]struct{})
set["a"] = struct{}{}
set["b"] = struct{}{}

// 映射
dict := make(map[string]int)
dict["key1"] = 1
dict["key2"] = 2

遍历数组、切片、集合(set)、映射(map)

在 Go 中,遍历数组有多种方法,可以使用 for 循环或者 range 关键字。下面是几种常见的遍历数组的方法:

使用 for 循环

package main

import "fmt"

func main() {
    // 定义一个数组
    arr := [5]int{1, 2, 3, 4, 5}

    // 使用 for 循环遍历数组
    for i := 0; i < len(arr); i++ {
        fmt.Println(arr[i])
    }
}

使用 range 关键字

package main

import "fmt"

func main() {
    // 定义一个数组
    arr := [5]int{1, 2, 3, 4, 5}

    // 使用 range 关键字遍历数组
    for index, value := range arr {
        fmt.Printf("Index: %d, Value: %d\n", index, value)
    }
}

结果

以上两种方法都会输出数组中的每个元素:

1
2
3
4
5

使用 range 关键字可以同时获取索引和值,更加方便。如果只需要值而不需要索引,可以使用下划线 _ 替代索引变量。

for _, value := range arr {
    fmt.Println(value)
}

以上就是在 Go 中遍历数组的几种常见方法。

在 Go 语言中,遍历切片、集合(使用 map[T]bool 实现的集合)和映射(map)可以使用 for 关键字结合 range 关键字来实现。以下是具体的示例代码:

遍历切片(Slice):

slice := []int{1, 2, 3, 4, 5}

for index, value := range slice {
    fmt.Printf("Index: %d, Value: %d\n", index, value)
}

在这个示例中,index 是切片的索引,value 是切片中对应索引位置的值。如果你只需要值,可以省略索引使用下划线 _

遍历集合(Set):

set := make(map[string]bool)
set["a"] = true
set["b"] = true
set["c"] = true

for key := range set {
    fmt.Printf("Key: %s\n", key)
}

在这个示例中,key 是集合中的元素,因为集合的值部分(bool 类型)在表示集合时通常不需要,所以我们只遍历键。

遍历映射(Map):

m := map[string]int{
    "a": 1,
    "b": 2,
    "c": 3,
}

for key, value := range m {
    fmt.Printf("Key: %s, Value: %d\n", key, value)
}

在这个示例中,key 是映射中的键,value 是映射中对应键的值。如果你只需要键或值,可以使用下划线 _ 来忽略不需要的部分。

这些示例展示了如何使用 forrange 遍历不同类型的数据结构。使用 range 关键字可以方便地遍历切片、集合和映射,并且能够同时获取索引和值或键和值。

文件IO读取对比

以下是使用表格展示Java和Go语言中文件I/O读写API的对比:

文件I/O APIJavaGo
文件读取FileReader, BufferedReaderos.Open, ioutil.ReadFile, bufio.NewScanner
文件写入FileWriter, BufferedWriteros.Create, os.OpenFile, ioutil.WriteFile
缓冲区读写BufferedReader, BufferedWriterbufio.NewReader, bufio.NewWriter
文件操作File, Filesos.File, os package
文件路径操作Paths, Paths.getfilepath, filepath package
文件信息获取File, File.listFiles, File.isDirectoryos.Stat, os.ReadDir, os.IsNotExist, os.IsExist

在这个表格中,Java和Go语言中的文件I/O API提供了类似的功能,但具体的实现方式和调用方法可能会有所不同。Java的文件I/O API主要在java.iojava.nio.file包中,而Go语言的文件I/O API主要在osio/ioutilbufio等包中。

Java

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

Go

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

file, err := os.Open("file.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
    log.Fatal(err)
}

并发控制对比

以下是使用表格展示Java和Go语言中主线程和子线程并发控制的对比:

并发控制JavaGo
协程(Goroutine)不适用,使用线程和Executor框架来实现并发使用协程(Goroutine)实现轻量级并发
线程(Thread)Thread, Executor, ThreadPoolExecutorgoroutine, sync package
互斥锁(Mutex)ReentrantLock, synchronizedMutex, sync package
读写锁(RWLock)ReentrantReadWriteLockRWMutex, sync package
条件变量(Condition)Condition, LockSupportCond, sync package
信号量(Semaphore)Semaphore不直接提供,可通过channel实现

在这个表格中,Java和Go语言提供了不同的并发控制机制来管理主线程和子线程之间的并发访问。Java中通常使用线程和Executor框架来实现并发控制,而Go语言则通过协程(Goroutine)来实现轻量级并发。同时,两种语言都提供了互斥锁、读写锁、条件变量和信号量等常用的并发控制工具,但具体的实现和使用方式可能会有所不同。

Java

// 使用 Thread
new Thread(() -> {
    System.out.println("Hello from thread!");
}).start();

Go

// 使用 goroutine
go func() {
    fmt.Println("Hello from goroutine!")
}()

锁对比

Java

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

Lock lock = new ReentrantLock();
lock.lock();
try {
    // critical section
} finally {
    lock.unlock();
}

Go

import (
    "sync"
)

var mu sync.Mutex
mu.Lock()
// critical section
mu.Unlock()

结合代码实例:并发控制 + 锁

Java

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    private static int counter = 0;
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(Main::increment);
        Thread t2 = new Thread(Main::increment);
        
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();
        
        System.out.println("Counter: " + counter);
    }

    private static void increment() {
        for (int i = 0; i < 1000; i++) {
            lock.lock();
            try {
                counter++;
            } finally {
                lock.unlock();
            }
        }
    }
}

Go

package main

import (
    "fmt"
    "sync"
)

var counter int
var mu sync.Mutex

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        mu.Lock()
        counter++
        mu.Unlock()
    }
}

func main() {
    var wg sync.WaitGroup

    wg.Add(2)
    go increment(&wg)
    go increment(&wg)

    wg.Wait()
    fmt.Println("Counter:", counter)
}

Go语言函数和方法的区别

以下是Go语言中函数和方法的区别和定义的详细对比:

特性函数 (Function)方法 (Method)
是否关联类型不与任何类型关联必须与某个类型关联
是否有接收者参数没有接收者参数有接收者参数
定义方式使用 func 关键字,不带接收者参数使用 func 关键字,带接收者参数
调用方式直接调用通过接收者类型的实例调用
用途实现独立的功能实现与某个类型关联的功能
语法示例func FunctionName(params) {}func (receiver ReceiverType) MethodName(params) {}

示例代码

函数的定义和使用

package main

import "fmt"

// 定义一个独立的函数
func Greet(name string) {
    fmt.Printf("Hello, my name is %s\n", name)
}

func main() {
    // 调用独立的函数
    Greet("Bob")
}

方法的定义和使用

package main

import "fmt"

// 定义一个结构体类型 Person
type Person struct {
    Name string
    Age  int
}

// 定义 Person 类型的方法
func (p Person) Greet() {
    fmt.Printf("Hello, my name is %s\n", p.Name)
}

func main() {
    // 创建一个 Person 实例
    person := Person{Name: "Alice", Age: 30}

    // 调用 Person 类型的方法
    person.Greet()
}

详细对比

  1. 关联类型

    • 函数:不与任何类型关联,是独立的。
    • 方法:必须与某个类型(可以是结构体、自定义类型等)关联。
  2. 接收者参数

    • 函数:没有接收者参数。
    • 方法:有接收者参数,接收者可以是值类型或指针类型。
  3. 定义方式

    • 函数:使用 func 关键字,不带接收者参数。
    • 方法:使用 func 关键字,带接收者参数。接收者参数在函数名称前面定义。
  4. 调用方式

    • 函数:直接通过函数名调用。
    • 方法:通过接收者类型的实例调用。
  5. 用途

    • 函数:用于实现独立的功能,适用于不需要与特定类型关联的操作。
    • 方法:用于实现与特定类型关联的功能,通常用于操作和访问类型的内部数据。

通过这种方式,Go语言实现了既支持面向对象编程的部分特性,又保持了函数式编程的简洁性和灵活性。

创建对象和构造函数

在Go语言中,尽管没有传统面向对象语言中的构造函数概念,但可以通过函数来创建和初始化结构体实例,从而实现类似构造函数的功能。这些函数通常被称为构造函数函数或工厂函数。

创建对象(结构体实例)

首先定义一个结构体类型:

type Person struct {
    Name string
    Age  int
}

使用工厂函数创建和初始化对象

可以定义一个工厂函数来创建和初始化 Person 结构体实例:

func NewPerson(name string, age int) Person {
    return Person{
        Name: name,
        Age:  age,
    }
}

使用这个工厂函数来创建 Person 实例:

func main() {
    person := NewPerson("Alice", 30)
    fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
}

使用指针返回对象

有时需要返回结构体的指针,特别是当你希望避免拷贝结构体或希望通过指针修改结构体的字段:

func NewPerson(name string, age int) *Person {
    return &Person{
        Name: name,
        Age:  age,
    }
}

使用这个版本的工厂函数:

func main() {
    person := NewPerson("Alice", 30)
    fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
}

初始化结构体的其他方式

直接初始化结构体:

func main() {
    person := Person{Name: "Alice", Age: 30}
    fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
}

使用 new 函数创建结构体的指针:

func main() {
    person := new(Person)
    person.Name = "Alice"
    person.Age = 30
    fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
}

总结

Go语言没有传统意义上的构造函数,但可以通过定义工厂函数来创建和初始化结构体实例。这种方式提供了灵活性,可以根据需要返回值类型或指针类型的结构体实例。直接初始化结构体和使用 new 函数也是创建结构体实例的常见方法。

方法定义

在Go语言中,方法是与特定类型(通常是结构体类型)相关联的函数。方法定义的方式与普通函数类似,但在方法名之前有一个接收者参数,用于指定该方法所属的类型。下面是详细的说明和示例。

基本结构

方法定义的基本结构如下:

func (receiver receiverType) MethodName(parameters) returnType {
    // 方法体
}
  • receiver 是接收者参数,可以理解为方法所属的类型的一个实例。
  • receiverType 是接收者的类型,可以是结构体类型或自定义类型。
  • MethodName 是方法名。
  • parameters 是方法参数列表。
  • returnType 是返回值类型,可以有多个返回值。

在Go语言中,方法是一种特殊的函数,它与某种类型关联,称为接收者(receiver)。接收者类似于其他语言中的面向对象编程中的实例方法,但在Go语言中,接收者既可以是指针类型也可以是值类型。这使得方法的接收者参数和接收者类型成为了独特的概念。

接收者类型:

接收者类型定义了方法所属的类型。在Go语言中,方法必须与某种类型关联,这种类型可以是任何类型,包括基本数据类型、结构体、接口等。例如:

type Circle struct {
    radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.radius * c.radius
}

在这个例子中,Circle 类型定义了一个圆的结构体,而 Area 方法是 Circle 类型的一个方法。

接收者参数:

接收者参数是方法定义中的一部分,它指定了方法调用时的接收者。在方法体内,接收者参数可以访问接收者类型的字段和方法。接收者参数可以是值类型也可以是指针类型。值类型接收者在方法调用时会进行值的拷贝,而指针类型接收者则可以修改接收者的值。例如:

func (c Circle) Area() float64 {
    return math.Pi * c.radius * c.radius
}

func (c *Circle) SetRadius(r float64) {
    c.radius = r
}

在这个例子中,Area 方法有一个值类型的接收者参数 c Circle,而 SetRadius 方法有一个指针类型的接收者参数 c *Circle

与Java不同

与Java不同的是,Go语言允许在任何类型上定义方法,而不仅仅是在类上。这使得方法的接收者参数和接收者类型成为了Go语言中独特的概念。通过使用方法,可以将函数与特定类型关联起来,从而实现了面向对象编程中的多态性和封装性。

示例

假设我们有一个结构体 Person,并定义一个方法 Greet 来打印问候语。

package main

import "fmt"

// 定义结构体
type Person struct {
    Name string
    Age  int
}

// 为结构体定义方法
func (p Person) Greet() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    person := Person{Name: "Alice", Age: 30}
    person.Greet() // 输出:Hello, my name is Alice
}

指针接收者和值接收者

Go语言的方法接收者可以是值接收者或指针接收者,这会影响方法对接收者数据的修改能力。

值接收者

值接收者的方法在调用时会接收者的一个副本,对该副本的修改不会影响原值。

func (p Person) ChangeName(newName string) {
    p.Name = newName
}

指针接收者

指针接收者的方法在调用时会接收者的地址,对该地址指向的数据进行修改会影响原值。

func (p *Person) ChangeName(newName string) {
    p.Name = newName
}

示例对比

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

// 值接收者
func (p Person) SetNameValue(newName string) {
    p.Name = newName
}

// 指针接收者
func (p *Person) SetNamePointer(newName string) {
    p.Name = newName
}

func main() {
    person := Person{Name: "Bob", Age: 25}
    
    person.SetNameValue("Charlie")
    fmt.Println(person.Name) // 输出:Bob,因为值接收者修改的是副本

    person.SetNamePointer("Charlie")
    fmt.Println(person.Name) // 输出:Charlie,因为指针接收者修改的是原值
}

多返回值

Go语言的方法可以返回多个值,通常用于返回结果和错误信息。

type Calculator struct{}

// 定义一个返回两个值的方法
func (c Calculator) Add(a, b int) (int, error) {
    return a + b, nil
}

func main() {
    calc := Calculator{}
    result, err := calc.Add(3, 4)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result) // 输出:Result: 7
    }
}

接口和方法实现

Go语言的接口是一组方法的集合。一个类型只要实现了接口中的所有方法,就实现了该接口。

type Greeter interface {
    Greet()
}

type Person struct {
    Name string
}

func (p Person) Greet() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    var greeter Greeter = Person{Name: "David"}
    greeter.Greet() // 输出:Hello, my name is David
}

方法嵌套

Go没有直接的方法嵌套,但你可以通过将一个方法调用另一个方法来实现类似的功能。

type Person struct {
    Name string
}

func (p Person) FirstName() string {
    // 假设Name格式为 "First Last"
    return strings.Split(p.Name, " ")[0]
}

func (p Person) Greet() {
    fmt.Println("Hello, my name is", p.FirstName())
}

func main() {
    person := Person{Name: "Emily Brown"}
    person.Greet() // 输出:Hello, my name is Emily
}

无返回值的方法

Go 示例

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

// 无返回值的方法
func (p Person) Greet() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    person := Person{Name: "Alice", Age: 30}
    person.Greet() // 输出:Hello, my name is Alice
}

Java 对比

public class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 无返回值的方法
    public void greet() {
        System.out.println("Hello, my name is " + name);
    }

    public static void main(String[] args) {
        Person person = new Person("Alice", 30);
        person.greet(); // 输出:Hello, my name is Alice
    }
}

无参数的方法

Go 示例

package main

import "fmt"

type Counter struct {
    Count int
}

// 无参数的方法
func (c *Counter) Increment() {
    c.Count++
}

func main() {
    counter := Counter{Count: 0}
    counter.Increment()
    fmt.Println(counter.Count) // 输出:1
}

Java 对比

public class Counter {
    int count;

    public Counter() {
        this.count = 0;
    }

    // 无参数的方法
    public void increment() {
        count++;
    }

    public static void main(String[] args) {
        Counter counter = new Counter();
        counter.increment();
        System.out.println(counter.count); // 输出:1
    }
}

无参数且无返回值的方法

Go 示例

package main

import "fmt"

type Greeter struct {
    Greeting string
}

// 无参数且无返回值的方法
func (g Greeter) Greet() {
    fmt.Println(g.Greeting)
}

func main() {
    greeter := Greeter{Greeting: "Hello, world!"}
    greeter.Greet() // 输出:Hello, world!
}

Java 对比

public class Greeter {
    String greeting;

    public Greeter(String greeting) {
        this.greeting = greeting;
    }

    // 无参数且无返回值的方法
    public void greet() {
        System.out.println(greeting);
    }

    public static void main(String[] args) {
        Greeter greeter = new Greeter("Hello, world!");
        greeter.greet(); // 输出:Hello, world!
    }
}

总结

  • 在Go和Java中,定义无返回值的方法时,Go省略了返回类型,Java使用void
  • 在Go和Java中,定义无参数的方法时,Go可以省略参数列表,Java需要空括号()
  • 在Go和Java中,定义既无参数也无返回值的方法时,Go省略了参数列表和返回类型,Java需要空括号()void

这些对比展示了Go语言在方法定义上的简洁性,同时也帮助你更好地理解如何在不同语言中定义类似的方法。

OOP

在Go语言中,虽然没有传统的面向对象编程(OOP)中的类和继承机制,但Go通过结构体(struct)和接口(interface)提供了类似的功能,可以实现类与类之间的关系,如组合、接口实现、多态等。下面是一些常见的Java类关系及其在Go中的实现方式。

1. 结构体和方法

Java

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void greet() {
        System.out.println("Hello, my name is " + name);
    }
}

Go

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func (p *Person) Greet() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    person := Person{Name: "Alice", Age: 30}
    person.Greet()
}

2. 组合(类似于继承)

Go通过组合来实现代码复用,这类似于Java的继承,但更灵活。

Java

public class Employee extends Person {
    private String company;

    public Employee(String name, int age, String company) {
        super(name, age);
        this.company = company;
    }

    public void work() {
        System.out.println("I work at " + company);
    }
}

Go

package main

import "fmt"

type Employee struct {
    Person
    Company string
}

func (e *Employee) Work() {
    fmt.Println("I work at", e.Company)
}

func main() {
    employee := Employee{
        Person:  Person{Name: "Bob", Age: 25},
        Company: "Acme Corp",
    }
    employee.Greet()
    employee.Work()
}

3. 接口和多态

Go通过接口实现多态,接口定义了一组方法,并由结构体实现。

Java

public interface Greeter {
    void greet();
}

public class Person implements Greeter {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public void greet() {
        System.out.println("Hello, my name is " + name);
    }
}

Go

package main

import "fmt"

type Greeter interface {
    Greet()
}

type Person struct {
    Name string
}

func (p Person) Greet() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    var greeter Greeter = Person{Name: "Charlie"}
    greeter.Greet()
}

4. 接口嵌套

Go接口可以嵌套,允许组合多个接口,这类似于Java的接口继承。

Java

public interface Worker {
    void work();
}

public interface Greeter {
    void greet();
}

public interface Employee extends Worker, Greeter {}

Go

package main

import "fmt"

type Worker interface {
    Work()
}

type Greeter interface {
    Greet()
}

type Employee interface {
    Worker
    Greeter
}

type Person struct {
    Name    string
    Company string
}

func (p Person) Work() {
    fmt.Println("I work at", p.Company)
}

func (p Person) Greet() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    var e Employee = Person{Name: "Dave", Company: "Tech Co"}
    e.Greet()
    e.Work()
}

5. 多态调用

多态是指通过接口进行方法调用,而具体的方法实现由具体的类型决定。

Java

public class Main {
    public static void main(String[] args) {
        Greeter greeter = new Person("Eve");
        greeter.greet();
    }
}

Go

package main

import "fmt"

type Greeter interface {
    Greet()
}

type Person struct {
    Name string
}

func (p Person) Greet() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    var greeter Greeter = Person{Name: "Eve"}
    greeter.Greet()
}

6. 抽象和实现分离

Go的接口机制让你可以很方便地分离抽象和具体实现。

Java

public abstract class Animal {
    public abstract void makeSound();
}

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.makeSound();
    }
}

Go

package main

import "fmt"

type Animal interface {
    MakeSound()
}

type Dog struct{}

func (d Dog) MakeSound() {
    fmt.Println("Woof")
}

func main() {
    var animal Animal = Dog{}
    animal.MakeSound()
}

通过这些例子,可以看到Go语言通过组合、接口等机制,能够实现类似Java中的类与类之间的关系和功能。这些Go的特性提供了灵活且高效的方式来设计和实现程序结构。