網頁

2021/7/3

Golang Interface 簡介

Go語言的Interface(介面)是由一組方法簽章(method signatures)組成的型態,其變數可儲存任意實現其方法組(method sets)的型態的值,目的為定義型態的行為。


使用關鍵字interface宣告介面。例如下面宣告Worker介面,其有一方法簽章為Eat()

type Worker interface { // declare interface Worker
    Eat()
}

當一型態(type)的方法(method)實作了介面中定義的方法簽章則該型態即實作了介面,此稱為隱式介面(implicit interfaces),因為非已明確宣告的方式來實作介面。

例如下面Go的Employee struct的方法Eat()實現了Worker.Eat(),也就是說Employee實作了Worker介面。

Go

// declare Worker interface
type Worker interface {
    Eat()
}

type Employee struct {
    Id int
    Name string
    Age int
}

func (e Employee) Eat() { 
    fmt.Println(e.Name + " eat snacks.")
}

相較於Java的介面要用implements關鍵字明確宣告來實作,所以是顯式介面(explicit interfaces)。下面則是Java的實作方式。

Java

public interface Worker {
    void eat();
}

public class Employee implements Worker{
    private long id;
    private String name;
    private int age;

    public void eat() {
        System.out.println(this.name + " eat snakes.")
    }
}

介面變數可儲存其實作型態的值。例如Worker的變數worker可以儲存Employee的值。

package main

import "fmt"

type Worker interface {
    Eat()
}

type Employee struct {
    Id int
    Name string
    Age int
}

// define Employee's method that implements Worker interface's method signature Eat()
func (e Employee) Eat() {
    fmt.Println(e.Name + " eat snacks.")
}

func main() {
    var worker Worker // define interface Worker's variable 'worker'

    employee := Employee{1, "John", 33} // create Employee value and assign to variable 'employee'
    worker = employee // interface variable can hold value of it's implementing type
    worker.Eat() // John eat snacks
}

若在Worker新增一個方法簽章Gossip(),但Employee只實現了Worker.Eat()而未實現Gossip(),則Employee沒有成功實作Worker介面,則將變數值分派到worker時會發生錯誤。換句話說,一個型態必須實現介面的全部方法簽章才算實作了該介面。

package main

import "fmt"

type Worker interface {
    Eat()
    Gossip(name, message string) string // add new method signature Gossip()
}

type Employee struct {
    Id int
    Name string
    Age int
}

func (e Employee) Eat() {
    fmt.Println(e.Name + " eat snacks")
}

func main() {
    var worker Worker

    employee := Employee{1, "John", 33}
    worker = employee // cannot use employee (type Employee) as type Worker in assignment:
                      // Employee does not implement Worker (missing Gossip method)
    worker.Eat()
}

下面加入Employee.Gossip()實現Worker.Gossip()完成實作。

package main

import "fmt"

type Worker interface {
    Eat()
    Gossip(name, message string) string
}

type Employee struct {
    Id int
    Name string
    Age int
}

func (e Employee) Eat() {
    fmt.Println(e.Name + " eat snacks")
}

// define Employee's method that implements Worker interface's method signature Gossip()
func (e Employee) Gossip(name, message string) string {
    return e.Name  + " said " + name + " " + message
}

func main() {
    var worker Worker

    employee := Employee{1, "John", 33}
    worker = employee
    worker.Eat() // John eat snacks
    gossip := worker.Gossip("Mary", "ugly")
    fmt.Println(gossip) // John said Mary ugly
}

介面不允許有兩個相同名稱的方法簽章。

type Worker interface {
    Eat() 
    Eat() // duplicate method Eat
    Gossip(name, message string) string
}

方法名稱相同的簽章參數不同一樣不允許。

type Worker interface {
    Eat() 
    Eat(food string) // duplicate method Eat
    Gossip(name, message string) string
}

方法簽章名稱不允許空白_命名。

type Worker interface {
    Eat() 
    _() // methods must have a unique non-blank name
    Gossip(name, message string) string
}

介面變數若無實作型態則呼叫方法會發生runtime error。

package main

import "fmt"

type Worker interface {
    Eat() 
}

func main() {
    var worker Worker
    fmt.Println(worker) // <nil>
    worker.Eat() // panic: runtime error: invalid memory address or nil pointer dereference
}

介面可以有多個型態實作。例如下面EmployeeManager都實作Worker

package main

import "fmt"

type Worker interface {
    Eat() 
}

type Employee struct {
    Id int
    Name string
    Age int
}

func (e Employee) Eat() {
    fmt.Println(e.Name + " eat snacks")
}

type Manager struct {
    Emp Employee
    Title string
}

func (e Manager) Eat() {
    fmt.Println(e.Title + " " + e.Emp.Name + " eat steaks")
}

func main() {
    var worker1 Worker
    employee := Employee{1, "John", 33}
    worker1 = employee
    worker1.Eat() // John eat snacks

    var worker2 Worker
    manager := Manager{
        Employee{2, "Mary", 44},
        "CTO",
    }
    worker2 = manager
    worker2.Eat() // CTO Mary eat steaks
}

一個型態可實作多個介面,例如下面Employee實作了WorkerPlayer介面。

package main

import "fmt"

type Worker interface {
    Eat() 
}

type Player interface {
    Curse()
}

type Employee struct {
    Id int
    Name string
    Age int
}

func (e Employee) Eat() {
    fmt.Println(e.Name + " eat snacks")
}

func (e Employee) Curse() {
    fmt.Println(e.Name + " curse partner idiot")
}

func main() {
    employee := Employee{1, "John", 33}
    
    var worker Worker
    worker = employee
    worker.Eat() // John eat snacks

    var player Player
    player = employee
    player.Curse() // John curse partner idiot
}

介面可集合介面,又稱embedded interface。例如下面Slashie介面集合了WorkerPlayer介面。

package main

import "fmt"

type Worker interface {
    Eat() 
}

type Player interface {
    Curse()
}

// union the method sets of Worker and Player interface
type Slashie interface {
    Worker
    Player
}

type Employee struct {
    Id int
    Name string
    Age int
}

func (e Employee) Eat() {
    fmt.Println(e.Name + " eat snacks")
}

func (e Employee) Curse() {
    fmt.Println(e.Name + " curse partner idiot")
}

func main() {
    var slashie Slashie
    employee := Employee{1, "John", 33}
    slashie = employee
    slashie.Eat() // John eat snacks
    slashie.Curse() // John curse partner idiot
}


沒有留言:

張貼留言